Archive this repo
diff --git a/.clang-format b/.clang-format deleted file mode 100644 index d5c7b08..0000000 --- a/.clang-format +++ /dev/null
@@ -1,3 +0,0 @@ -# Defines the Chromium style for automatic reformatting. -# http://clang.llvm.org/docs/ClangFormatStyleOptions.html -BasedOnStyle: Chromium
diff --git a/.codespellignorelines b/.codespellignorelines deleted file mode 100644 index 0c7fa5f..0000000 --- a/.codespellignorelines +++ /dev/null
@@ -1,10 +0,0 @@ - vp9AllowList.get("Technicolor").add("STING"); - return SkSurface::MakeRenderTarget(gr_context_.get(), SkBudgeted::kNo, - texture_infos.push_back(TextureInfo("uv", "ba")); -/* If zlib is available and you want to support the flate compression -// Onces represent initializations that should only ever happen once per - gr_texture_.reset(new GrBackendTexture(texture_size.width(), - texture_size.height(), - GrMipMapped::kNo, texture_info)); -Onces represent initializations that should only ever happen once per process, - <Resource Language="TE" />
diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index 3b65cb7..0000000 --- a/.coveragerc +++ /dev/null
@@ -1,13 +0,0 @@ -# pycov configuration - -[run] -source=. - -omit= - # Exclude test files from coverage report - *_test.py - cobalt/black_box_tests/tests/* - cobalt/media_integration_tests/* - - # Exclude scripts in third_party/ - third_party/*
diff --git a/.gn b/.gn deleted file mode 100644 index 5fb6ec2..0000000 --- a/.gn +++ /dev/null
@@ -1,21 +0,0 @@ -# Copyright 2021 The Cobalt Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# The location of the build configuration file. -buildconfig = "//starboard/build/config/BUILDCONFIG.gn" - -# We have `python3` if we're in a container or on buildbot, so use that. -if (getenv("IS_DOCKER") == "1" || getenv("BUILDBOT_PROJECT") != "") { - script_executable = "python3" -}
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml deleted file mode 100644 index 4a945f0..0000000 --- a/.pre-commit-config.yaml +++ /dev/null
@@ -1,204 +0,0 @@ -# See https://pre-commit.com for more information -# See https://pre-commit.com/hooks.html for more hooks -default_stages: [commit] - -default_language_version: - python: python3 - -files: | - (?x)^( - ( - .github| - cobalt| - docker| - internal| - precommit_hooks| - starboard| - tools/metrics/actions/cobalt| - tools/metrics/histograms/metadata/cobalt| - )/ - | ^[^/]+$ # files in the root - ) -exclude: | - (?x)^( - ( - internal/kokoro/third-party| - internal/starboard/shared/glimp/include| - internal/starboard/shared/playstation/glimp/shaders| - )/ - | - .*_pb2\.py$ | - .*\.pb\.cc$ | - .*\.pb\.h$ | - .*\.patch$ | - .*Proto.java$| - .*\.sig$ - ) - -repos: -- repo: https://cobalt.googlesource.com/pre-commit-hooks - rev: e1668fe86af3810fbca72b8653fe478e66a0afdc # v3.2.0 - hooks: - - id: check-case-conflict - - id: end-of-file-fixer - - id: trailing-whitespace - - id: mixed-line-ending - - id: check-xml - -- repo: https://cobalt.googlesource.com/codespell - rev: 67c489d36dd4c52cbb9e4755d90c35c6231842ef # v2.0.0 - hooks: - - id: codespell - name: Spell Check - args: [-x, .codespellignorelines, - # The --ignore-words-list argument has a bug where it needs to - # be lowercase, see - # https://github.com/codespell-project/codespell/issues/1390 - --ignore-words-list, "atleast,varius,ridiculus,statics", - ] - exclude: | - (?x)^( - (internal/)?starboard/[^/]+/i18n/| - cobalt/content/licenses/| - cobalt/content/ssl/| - cobalt/fetch/embedded_scripts| - cobalt/streams/embedded_scripts| - cobalt/loader/cors_preflight.cc| - internal/cobalt/browser/splash_screen/youtube_splash_screen.html| - internal/starboard/shared/playstation/storage_internal.cc| - starboard/loader_app/app_key_test.cc| - starboard/shared/starboard/player/testdata| - starboard/shared/win32/media_transform.cc - ) - -- repo: local - hooks: - - id: sync-keyboxes - name: sync-keyboxes - entry: python ./precommit_hooks/sync_keyboxes_wrapper.py - language: python - stages: [post-checkout] - always_run: true - - id: clang-format - name: clang-format - entry: clang-format - language: python - types: [c++] - exclude_types: [objective-c++] - args: [-i, -style=file] - additional_dependencies: ['clang-format'] - - id: cpplint - name: cpplint - entry: cpplint - language: system - types: [c++] - args: [--verbose=4, --quiet] - exclude: | - (?x)( - ^cobalt/bindings/(templates|generated)/| - ^internal/starboard/shared/uikit/.*\.h$ - ) - - id: yapf - name: yapf - description: Run yapf (the python formatter) in-place on changed files. - entry: yapf - language: system - types: [python] - args: [-i, -vv] - - id: pylint - name: pylint - entry: pylint - language: system - types: [python] - args: [-d W0201] - - id: google-java-format - name: google-java-format - entry: python ./precommit_hooks/google_java_format_wrapper.py - language: python - types: [java] - args: [-i] - - id: gcheckstyle - name: Lint Java With gcheckstyle - entry: python ./precommit_hooks/gcheckstyle_wrapper.py - language: python - types: [java] - verbose: true - - id: check-copyright-year - name: check copyright year - entry: python ./precommit_hooks/check_copyright_year.py - language: python - types: [file, text] - stages: [push] - exclude: | - (?x)^( - .pylintrc$| - .pre-commit-config.yaml$| - cobalt/base/unicode/| - cobalt/demos/content/| - cobalt/layout_tests/testdata/| - cobalt/math/| - cobalt/media/| - cobalt/streams/| - cobalt/webdriver/screencast/| - cobalt/websocket/| - components/crx_file/| - content/| - internal/starboard/shared/glimp/| - nb/| - starboard/android/apk/gradlew| - starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaDrmBridge.java$| - starboard/shared/starboard/player/filter/decoded_audio_queue.cc$| - starboard/shared/starboard/player/filter/decoded_audio_queue.h$| - starboard/shared/starboard/player/testdata/licences/| - starboard/shared/starboard/player/filter/wsola_internal.cc$ - ) - exclude_types: [markdown] - verbose: true - - id: check-if-starboard-interface-changed - name: check if starboard interface changed - entry: python ./precommit_hooks/warn_that_starboard_interface_changed_wrapper.py - language: python - files: '^starboard/[^/]+\.h$' - stages: [push] - - id: download-resources - name: download resources - entry: python ./download_resources.py - language: python - stages: [post-checkout] - always_run: true - - id: test-download-from-gcs-helper - name: test download from gcs helper - entry: python - language: python - pass_filenames: false - always_run: true - stages: [push] - args: [-m, unittest, cobalt/tools/download_from_gcs_test.py] - - id: test-python3-compatibility - name: Test Python 3 Compatibility - description: Checks that scripts can be run in Python 3 - entry: python precommit_hooks/python3_check.py - language: python - types: [python] - verbose: true - - id: check-bug-in-commit-message - name: Check Bug In Commit Message - description: Checks for a tracking bug in a commit message - entry: python precommit_hooks/check_bug_in_description_wrapper.py - language: python - stages: [push] - always_run: true - pass_filenames: false - - id: gn-format - name: GN format - entry: gn format - language: system - files: '.*\.gni?$' - - id: meta-validate - name: Validate METADATA files - entry: python -m cobalt.tools.metadata.validate - language: python - additional_dependencies: - - "protobuf" - always_run: true - pass_filenames: false
diff --git a/.pylintrc b/.pylintrc deleted file mode 100644 index 3707dea..0000000 --- a/.pylintrc +++ /dev/null
@@ -1,431 +0,0 @@ -# This Pylint rcfile contains a best-effort configuration to uphold the -# best-practices and style described in the Google Python style guide: -# https://google.github.io/styleguide/pyguide.html -# -# Its canonical open-source location is: -# https://google.github.io/styleguide/pylintrc -# -# Last updated for Cobalt (YYYY-MM-DD): 2022-12-29 - -[MASTER] - -# Files or directories to be skipped. They should be base names, not paths. -ignore=third_party - -# Files or directories matching the regex patterns are skipped. The regex -# matches against base names, not paths. -ignore-patterns= - -# Pickle collected data for later comparisons. -persistent=no - -# List of plugins (as comma separated values of python modules names) to load, -# usually to register additional checkers. -load-plugins= - -# Use multiple processes to speed up Pylint. -jobs=4 - -# Allow loading of arbitrary C extensions. Extensions are imported into the -# active Python interpreter and may run arbitrary code. -unsafe-load-any-extension=no - - -[MESSAGES CONTROL] - -# Only show warnings with the listed confidence levels. Leave empty to show -# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED -confidence= - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time (only on the command line, not in the configuration file where -# it should appear only once). See also the "--disable" option for examples. -#enable= - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifiers separated by comma (,) or put this -# option multiple times (only on the command line, not in the configuration -# file where it should appear only once).You can also use "--disable=all" to -# disable everything first and then reenable specific checks. For example, if -# you want to run only the similarities checker, you can use "--disable=all -# --enable=similarities". If you want to run only the classes checker, but have -# no Warning level messages displayed, use"--disable=all --enable=classes -# --disable=W" -disable=abstract-method, - apply-builtin, - arguments-differ, - attribute-defined-outside-init, - backtick, - bad-option-value, - basestring-builtin, - buffer-builtin, - c-extension-no-member, - consider-using-enumerate, - cmp-builtin, - cmp-method, - coerce-builtin, - coerce-method, - delslice-method, - div-method, - duplicate-code, - eq-without-hash, - execfile-builtin, - file-builtin, - filter-builtin-not-iterating, - fixme, - getslice-method, - global-statement, - hex-method, - idiv-method, - implicit-str-concat, - import-error, - import-self, - import-star-module-level, - inconsistent-return-statements, - input-builtin, - intern-builtin, - invalid-str-codec, - locally-disabled, - long-builtin, - long-suffix, - map-builtin-not-iterating, - misplaced-comparison-constant, - missing-function-docstring, - metaclass-assignment, - next-method-called, - next-method-defined, - no-absolute-import, - no-else-break, - no-else-continue, - no-else-raise, - no-else-return, - no-init, # added - no-member, - no-name-in-module, - no-self-use, - nonzero-method, - oct-method, - old-division, - old-ne-operator, - old-octal-literal, - old-raise-syntax, - parameter-unpacking, - print-statement, - raising-string, - range-builtin-not-iterating, - raw_input-builtin, - rdiv-method, - reduce-builtin, - relative-import, - reload-builtin, - round-builtin, - setslice-method, - signature-differs, - standarderror-builtin, - suppressed-message, - sys-max-int, - too-few-public-methods, - too-many-ancestors, - too-many-arguments, - too-many-boolean-expressions, - too-many-branches, - too-many-instance-attributes, - too-many-locals, - too-many-nested-blocks, - too-many-public-methods, - too-many-return-statements, - too-many-statements, - trailing-newlines, - unichr-builtin, - unicode-builtin, - unnecessary-pass, - unpacking-in-except, - useless-else-on-loop, - useless-object-inheritance, - useless-suppression, - using-cmp-argument, - wrong-import-order, - xrange-builtin, - zip-builtin-not-iterating, - - -[REPORTS] - -# Set the output format. Available formats are text, parseable, colorized, msvs -# (visual studio) and html. You can also give a reporter class, eg -# mypackage.mymodule.MyReporterClass. -output-format=text - -# Tells whether to display a full report or only the messages -reports=no - -# Python expression which should return a note less than 10 (10 is the highest -# note). You have access to the variables errors warning, statement which -# respectively contain the number of errors / warnings messages and the total -# number of statements analyzed. This is used by the global evaluation report -# (RP0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Template used to display messages. This is a python new-style format string -# used to format the message information. See doc for all details -#msg-template= - - -[BASIC] - -# Good variable names which should always be accepted, separated by a comma -good-names=main,_ - -# Bad variable names which should always be refused, separated by a comma -bad-names= - -# Colon-delimited sets of names that determine each other's naming style when -# the name regexes allow several styles. -name-group= - -# Include a hint for the correct naming format with invalid-name -include-naming-hint=no - -# List of decorators that produce properties, such as abc.abstractproperty. Add -# to this list to register other decorators that produce valid properties. -property-classes=abc.abstractproperty,cached_property.cached_property,cached_property.threaded_cached_property,cached_property.cached_property_with_ttl,cached_property.threaded_cached_property_with_ttl - -# Regular expression matching correct function names -function-rgx=^(?:(?P<exempt>setUp|tearDown|setUpModule|tearDownModule)|(?P<camel_case>_?[A-Z][a-zA-Z0-9]*)|(?P<snake_case>_?[a-z][a-z0-9_]*))$ - -# Regular expression matching correct variable names -variable-rgx=^[a-z][a-z0-9_]*$ - -# Regular expression matching correct constant names -const-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$ - -# Regular expression matching correct attribute names -attr-rgx=^_{0,2}[a-z][a-z0-9_]*$ - -# Regular expression matching correct argument names -argument-rgx=^[a-z][a-z0-9_]*$ - -# Regular expression matching correct class attribute names -class-attribute-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$ - -# Regular expression matching correct inline iteration names -inlinevar-rgx=^[a-z][a-z0-9_]*$ - -# Regular expression matching correct class names -class-rgx=^_?[A-Z][a-zA-Z0-9]*$ - -# Regular expression matching correct module names -module-rgx=^(_?[a-z][a-z0-9_]*|__init__|2)$ - -# Regular expression matching correct method names -method-rgx=(?x)^(?:(?P<exempt>_[a-z0-9_]+__|runTest|setUp|tearDown|setUpTestCase|tearDownTestCase|setupSelf|tearDownClass|setUpClass|(test|assert)_*[A-Z0-9][a-zA-Z0-9_]*|next)|(?P<camel_case>_{0,2}[A-Z][a-zA-Z0-9_]*)|(?P<snake_case>_{0,2}[a-z][a-z0-9_]*))$ - -# Regular expression which should only match function or class names that do -# not require a docstring. -no-docstring-rgx=(__.*__|main|test.*|.*test|.*Test)$ - -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=10 - - -[TYPECHECK] - -# List of decorators that produce context managers, such as -# contextlib.contextmanager. Add to this list to register other decorators that -# produce valid context managers. -contextmanager-decorators=contextlib.contextmanager,contextlib2.contextmanager - -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis. It -# supports qualified module names, as well as Unix pattern matching. -ignored-modules= - -# List of class names for which member attributes should not be checked (useful -# for classes with dynamically set attributes). This supports the use of -# qualified names. -ignored-classes=optparse.Values,thread._local,_thread._local - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E1101 when accessed. Python regular -# expressions are accepted. -generated-members= - - -[FORMAT] - -# Maximum number of characters on a single line. -max-line-length=80 - -# TODO(https://github.com/PyCQA/pylint/issues/3352): Direct pylint to exempt -# lines made too long by directives to pytype. - -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=(?x)( - ^\s*(\#\ )?<?https?://\S+>?$| - ^\s*(from\s+\S+\s+)?import\s+.+$) - -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=yes - -# Maximum number of lines in a module -max-module-lines=99999 - -# String used as indentation unit. The internal Google style guide mandates 2 -# spaces. Google's externaly-published style guide says 4, consistent with -# PEP 8. Here, we use 2 spaces, for conformity with many open-sourced Google -# projects (like TensorFlow). -indent-string=' ' - -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 - -# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. -expected-line-ending-format= - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes=TODO - - -[STRING] - -# This flag controls whether inconsistent-quotes generates a warning when the -# character used as a quote delimiter is used inconsistently within a module. -check-quote-consistency=yes - - -[VARIABLES] - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# A regular expression matching the name of dummy variables (i.e. expectedly -# not used). -dummy-variables-rgx=^\*{0,2}(_$|unused_|dummy_) - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid to define new builtins when possible. -additional-builtins= - -# List of strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks=cb_,_cb - -# List of qualified module names which can have objects that can redefine -# builtins. -redefining-builtins-modules=six,six.moves,past.builtins,future.builtins,functools - - -[LOGGING] - -# Logging modules to check that the string format arguments are in logging -# function parameter format -logging-modules=logging,absl.logging,tensorflow.io.logging - - -[SIMILARITIES] - -# Minimum lines number of a similarity. -min-similarity-lines=4 - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - -# Ignore imports when computing similarities. -ignore-imports=no - - -[SPELLING] - -# Spelling dictionary name. Available dictionaries: none. To make it working -# install python-enchant package. -spelling-dict= - -# List of comma separated words that should not be checked. -spelling-ignore-words= - -# A path to a file that contains private dictionary; one word per line. -spelling-private-dict-file= - -# Tells whether to store unknown words to indicated private dictionary in -# --spelling-private-dict-file option instead of raising a message. -spelling-store-unknown-words=no - - -[IMPORTS] - -# Deprecated modules which should not be used, separated by a comma -deprecated-modules=regsub, - TERMIOS, - Bastion, - rexec, - sets - -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report RP0402 must not be disabled) -import-graph= - -# Create a graph of external dependencies in the given file (report RP0402 must -# not be disabled) -ext-import-graph= - -# Create a graph of internal dependencies in the given file (report RP0402 must -# not be disabled) -int-import-graph= - -# Force import order to recognize a module as part of the standard -# compatibility libraries. -known-standard-library= - -# Force import order to recognize a module as part of a third party library. -known-third-party=enchant, absl - -# Analyse import fallback blocks. This can be used to support both Python 2 and -# 3 compatible code, which means that the block might have code that exists -# only in one or another interpreter, leading to false positives when analysed. -analyse-fallback-blocks=no - - -[CLASSES] - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__, - __new__, - setUp - -# List of member names, which should be excluded from the protected access -# warning. -exclude-protected=_asdict, - _fields, - _replace, - _source, - _make - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls, - class_ - -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=mcs - - -[EXCEPTIONS] - -# Exceptions that will emit a warning when being caught. Defaults to -# "Exception" -overgeneral-exceptions=builtins.StandardError, - builtins.Exception, - builtins.BaseException
diff --git a/.style.yapf b/.style.yapf deleted file mode 100644 index fdd0723..0000000 --- a/.style.yapf +++ /dev/null
@@ -1,2 +0,0 @@ -[style] -based_on_style = yapf
diff --git a/BUILD.gn b/BUILD.gn deleted file mode 100644 index d2d4ccd..0000000 --- a/BUILD.gn +++ /dev/null
@@ -1,35 +0,0 @@ -# Copyright 2021 The Cobalt Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -group("gn_all") { - testonly = true - - deps = [ - "//cobalt:gn_all", - "//starboard:gn_all", - ] -} - -group("default") { - testonly = true - - if (is_qa || is_gold) { - deps = [ - "//cobalt:default", - "//starboard:default", - ] - } else { - deps = [ ":gn_all" ] - } -}
diff --git a/BUILD_STATUS.md b/BUILD_STATUS.md deleted file mode 100644 index 560e001..0000000 --- a/BUILD_STATUS.md +++ /dev/null
@@ -1,48 +0,0 @@ -# Build Status - -## Post-submit - -| Workflow | Main | 25.lts.1+ | 24.lts.1+ | 23.lts.1+ | -| --------- | ---- | --------- | --------- | --------- | -| Lint | [](https://github.com/youtube/cobalt/actions/workflows/lint.yaml?query=event%3Apush+branch%3Amain) | | | | -| Android | [](https://github.com/youtube/cobalt/actions/workflows/android.yaml?query=event%3Apush+branch%3Amain) | [](https://github.com/youtube/cobalt/actions/workflows/android_25.lts.1+.yaml?query=event%3Apush+branch%3A25.lts.1%2B) | [](https://github.com/youtube/cobalt/actions/workflows/android_24.lts.1+.yaml?query=event%3Apush+branch%3A24.lts.1%2B) | [](https://github.com/youtube/cobalt/actions/workflows/android_23.lts.1+.yaml?query=event%3Apush+branch%3A23.lts.1%2B) | -| Evergreen | [](https://github.com/youtube/cobalt/actions/workflows/evergreen.yaml?query=event%3Apush+branch%3Amain) | [](https://github.com/youtube/cobalt/actions/workflows/evergreen_25.lts.1+.yaml?query=event%3Apush+branch%3A25.lts.1%2B) | [](https://github.com/youtube/cobalt/actions/workflows/evergreen_24.lts.1+.yaml?query=event%3Apush+branch%3A24.lts.1%2B) | [](https://github.com/youtube/cobalt/actions/workflows/evergreen_23.lts.1+.yaml?query=event%3Apush+branch%3A23.lts.1%2B) | -| Linux | [](https://github.com/youtube/cobalt/actions/workflows/linux.yaml?query=event%3Apush+branch%3Amain) | [](https://github.com/youtube/cobalt/actions/workflows/linux_25.lts.1+.yaml?query=event%3Apush+branch%3A25.lts.1%2B) | [](https://github.com/youtube/cobalt/actions/workflows/linux_24.lts.1+.yaml?query=event%3Apush+branch%3A24.lts.1%2B) | [](https://github.com/youtube/cobalt/actions/workflows/linux_23.lts.1+.yaml?query=event%3Apush+branch%3A23.lts.1%2B) | -| Raspi-2 | [](https://github.com/youtube/cobalt/actions/workflows/raspi-2.yaml?query=event%3Apush+branch%3Amain) | [](https://github.com/youtube/cobalt/actions/workflows/raspi-2_25.lts.1+.yaml?query=event%3Apush+branch%3A25.lts.1%2B) | [](https://github.com/youtube/cobalt/actions/workflows/raspi-2_24.lts.1+.yaml?query=event%3Apush+branch%3A24.lts.1%2B) | [](https://github.com/youtube/cobalt/actions/workflows/raspi-2_23.lts.1+.yaml?query=event%3Apush+branch%3A23.lts.1%2B) | -| Stub | [](https://github.com/youtube/cobalt/actions/workflows/stub.yaml?query=event%3Apush+branch%3Amain) | [](https://github.com/youtube/cobalt/actions/workflows/stub_25.lts.1+.yaml?query=event%3Apush+branch%3A25.lts.1%2B) | [](https://github.com/youtube/cobalt/actions/workflows/stub_24.lts.1+.yaml?query=event%3Apush+branch%3A24.lts.1%2B) | [](https://github.com/youtube/cobalt/actions/workflows/stub_23.lts.1+.yaml?query=event%3Apush+branch%3A23.lts.1%2B) | -| Win32 | [](https://github.com/youtube/cobalt/actions/workflows/win32.yaml?query=event%3Apush+branch%3Amain) | [](https://github.com/youtube/cobalt/actions/workflows/win32_25.lts.1+.yaml?query=event%3Apush+branch%3A25.lts.1%2B) | [](https://github.com/youtube/cobalt/actions/workflows/win32_24.lts.1+.yaml?query=event%3Apush+branch%3A24.lts.1%2B) | [](https://github.com/youtube/cobalt/actions/workflows/win32_23.lts.1+.yaml?query=event%3Apush+branch%3A23.lts.1%2B) | -| Xbox One | [](https://github.com/youtube/cobalt/actions/workflows/xb1.yaml?query=event%3Apush+branch%3Amain) | [](https://github.com/youtube/cobalt/actions/workflows/xb1_25.lts.1+.yaml?query=event%3Apush+branch%3A25.lts.1%2B) | | | -| Python | [](https://github.com/youtube/cobalt/actions/workflows/pytest.yaml?query=event%3Apush+branch%3Amain) | [](https://github.com/youtube/cobalt/actions/workflows/pytest_25.lts.1+.yaml?query=event%3Apush+branch%3A25.lts.1%2B) | | | -| Java | [](https://github.com/youtube/cobalt/actions/workflows/gradle.yaml?query=event%3Apush+branch%3Amain) | [](https://github.com/youtube/cobalt/actions/workflows/gradle_25.lts.1+.yaml?query=event%3Apush+branch%3A25.lts.1%2B) | | | - -## Nightly builds - -| Workflow | main | 25.lts.1+ | 24.lts.1+ | 23.lts.1+ | -| --------- | ---- | --------- | --------- | --------- | -| Android | [](https://github.com/youtube/cobalt/actions/workflows/android.yaml?query=event%3Aschedule+branch%3Amain) | [](https://github.com/youtube/cobalt/actions/workflows/android_25.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A25.lts.1%2B) | [](https://github.com/youtube/cobalt/actions/workflows/android_24.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A24.lts.1%2B) | [](https://github.com/youtube/cobalt/actions/workflows/android_23.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A23.lts.1%2B) | -| Evergreen | [](https://github.com/youtube/cobalt/actions/workflows/evergreen.yaml?query=event%3Aschedule+branch%3Amain) | [](https://github.com/youtube/cobalt/actions/workflows/evergreen_25.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A25.lts.1%2B) | [](https://github.com/youtube/cobalt/actions/workflows/evergreen_24.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A24.lts.1%2B) | [](https://github.com/youtube/cobalt/actions/workflows/evergreen_23.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A23.lts.1%2B) | -| Linux | [](https://github.com/youtube/cobalt/actions/workflows/linux.yaml?query=event%3Aschedule+branch%3Amain) | [](https://github.com/youtube/cobalt/actions/workflows/linux_25.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A25.lts.1%2B) | [](https://github.com/youtube/cobalt/actions/workflows/linux_24.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A24.lts.1%2B) | [](https://github.com/youtube/cobalt/actions/workflows/linux_23.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A23.lts.1%2B) | -| Raspi-2 | [](https://github.com/youtube/cobalt/actions/workflows/raspi-2.yaml?query=event%3Aschedule+branch%3Amain) | [](https://github.com/youtube/cobalt/actions/workflows/raspi-2_25.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A25.lts.1%2B) | [](https://github.com/youtube/cobalt/actions/workflows/raspi-2_24.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A24.lts.1%2B) | [](https://github.com/youtube/cobalt/actions/workflows/raspi-2_23.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A23.lts.1%2B) | -| Win32 | [](https://github.com/youtube/cobalt/actions/workflows/win32.yaml?query=event%3Aschedule+branch%3Amain) | [](https://github.com/youtube/cobalt/actions/workflows/win32_25.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A25.lts.1%2B) | [](https://github.com/youtube/cobalt/actions/workflows/win32_24.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A24.lts.1%2B) | [](https://github.com/youtube/cobalt/actions/workflows/win32_23.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A23.lts.1%2B) | -| Xbox One | [](https://github.com/youtube/cobalt/actions/workflows/xb1.yaml?query=event%3Aschedule+branch%3Amain) | [](https://github.com/youtube/cobalt/actions/workflows/xb1_25.lts.1+.yaml?query=event%3Aschedule+branch%3A24.lts.1%2B) | | | - - -# Legacy releases - -## Post-submit - -| 22.lts.1+ | 21.lts.1+ | 20.lts.1+ | 19.lts.1+ | RC11 | COBALT 9 | -| --------- | --------- | --------- | --------- | ---- | -------- | -| [](https://github.com/youtube/cobalt/actions/workflows/android_22.lts.1+.yaml?query=branch%3A22.lts.1%2B+event%3Apush) | | | | | | -| [](https://github.com/youtube/cobalt/actions/workflows/evergreen_22.lts.1+.yaml?query=branch%3A22.lts.1%2B+event%3Apush) | [](https://github.com/youtube/cobalt/actions/workflows/evergreen_21.lts.1+.yaml?query=branch%3A21.lts.1%2B+event%3Apush) | | | | | -| [](https://github.com/youtube/cobalt/actions/workflows/linux_22.lts.1+.yaml?query=branch%3A22.lts.1%2B+event%3Apush) | [](https://github.com/youtube/cobalt/actions/workflows/linux_21.lts.1+.yaml?query=branch%3A21.lts.1%2B+event%3Apush) | [](https://github.com/youtube/cobalt/actions/workflows/linux_20.lts.1+.yaml?query=branch%3A20.lts.1%2B+event%3Apush) | [](https://github.com/youtube/cobalt/actions/workflows/linux_19.lts.1+.yaml?query=branch%3A19.lts.1%2B+event%3Apush) | [](https://github.com/youtube/cobalt/actions/workflows/linux_rc_11.yaml?query=event%3Apush+branch%3Arc_11) | [](https://github.com/youtube/cobalt/actions/workflows/linux_COBALT_9.yaml?query=event%3Apush+branch%3ACOBALT_9) | -| [](https://github.com/youtube/cobalt/actions/workflows/raspi-2_22.lts.1+.yaml?query=branch%3A22.lts.1%2B+event%3Apush) | [](https://github.com/youtube/cobalt/actions/workflows/raspi-2_21.lts.1+.yaml?query=branch%3A21.lts.1%2B+event%3Apush) | [](https://github.com/youtube/cobalt/actions/workflows/raspi-2_20.lts.1+.yaml?query=branch%3A20.lts.1%2B+event%3Apush) | [](https://github.com/youtube/cobalt/actions/workflows/raspi-2_19.lts.1+.yaml?query=branch%3A19.lts.1%2B+event%3Apush) | [](https://github.com/youtube/cobalt/actions/workflows/raspi-2_rc_11.yaml?query=event%3Apush+branch%3Arc_11) | [](https://github.com/youtube/cobalt/actions/workflows/raspi-2_COBALT_9.yaml?query=event%3Apush+branch%3ACOBALT_9) | - -## Nightly - -| 22.lts.1+ | 21.lts.1+ | 20.lts.1+ | 19.lts.1+ | RC11 | COBALT 9 | -| --------- | --------- | --------- | --------- | ---- | --------- | -| [](https://github.com/youtube/cobalt/actions/workflows/android_22.lts.1+.yaml?query=branch%3A22.lts.1%2B+event%3Aworkflow_dispatch) | | | | | | -| [](https://github.com/youtube/cobalt/actions/workflows/evergreen_22.lts.1+.yaml?query=branch%3A22.lts.1%2B+event%3Aworkflow_dispatch) | [](https://github.com/youtube/cobalt/actions/workflows/evergreen_21.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A21.lts.1%2B) | | | | | -| [](https://github.com/youtube/cobalt/actions/workflows/linux_22.lts.1+.yaml?query=branch%3A22.lts.1%2B+event%3Aworkflow_dispatch) | [](https://github.com/youtube/cobalt/actions/workflows/linux_21.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A21.lts.1%2B) | [](https://github.com/youtube/cobalt/actions/workflows/linux_20.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A20.lts.1%2B) | [](https://github.com/youtube/cobalt/actions/workflows/linux_19.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A19.lts.1%2B) | [](https://github.com/youtube/cobalt/actions/workflows/linux_rc_11.yaml?query=event%3Aworkflow_dispatch+branch%3Arc_11) | [](https://github.com/youtube/cobalt/actions/workflows/linux_COBALT_9.yaml?query=event%3Aworkflow_dispatch+branch%3ACOBALT_9) | -| [](https://github.com/youtube/cobalt/actions/workflows/raspi-2_22.lts.1+.yaml?query=branch%3A22.lts.1%2B+event%3Aworkflow_dispatch) | [](https://github.com/youtube/cobalt/actions/workflows/raspi-2_21.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A21.lts.1%2B) | [](https://github.com/youtube/cobalt/actions/workflows/raspi-2_20.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A20.lts.1%2B) | [](https://github.com/youtube/cobalt/actions/workflows/raspi-2_19.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A19.lts.1%2B) | [](https://github.com/youtube/cobalt/actions/workflows/raspi-2_rc_11.yaml?query=event%3Aworkflow_dispatch+branch%3Arc_11) | [](https://github.com/youtube/cobalt/actions/workflows/raspi-2_COBALT_9.yaml?query=event%3Aworkflow_dispatch+branch%3ACOBALT_9) |
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 0604bc7..0000000 --- a/CONTRIBUTING.md +++ /dev/null
@@ -1,99 +0,0 @@ -# Contributing guidelines - -## Pull Request Checklist - -Before sending your pull requests, make sure you do the following: - -- Read the [contributing guidelines](CONTRIBUTING.md). -- Ensure you have signed the - [Contributor License Agreement (CLA)](https://cla.developers.google.com/). -- Check if your changes are consistent with the: - - [General guidelines](#general-guidelines-and-philosophy-for-contribution). - - [Coding Style](#coding-style). -- Run the [unit tests](#running-unit-tests). - -### Contributor License Agreements - -We'd love to accept your patches! Before we can take them, we have to jump a couple of legal hurdles. - -Please fill out either the individual or corporate Contributor License Agreement (CLA). - - * If you are an individual writing original source code and you're sure you own the intellectual property, then you'll need to sign an [individual CLA](https://code.google.com/legal/individual-cla-v1.0.html). - * If you work for a company that wants to allow you to contribute your work, then you'll need to sign a [corporate CLA](https://code.google.com/legal/corporate-cla-v1.0.html). - -Follow either of the two links above to access the appropriate CLA and instructions for how to sign and return it. Once we receive it, we'll be able to accept your pull requests. - -***NOTE***: Only original source code from you and other people that have signed the CLA can be accepted into the main repository. - -### Community Guidelines - -This project follows -[Google's Open Source Community Guidelines](https://opensource.google/conduct/). - -### Contributing code - -If you have improvements to Cobalt, send us your pull requests! For those -just getting started, Github has a -[how to](https://help.github.com/articles/using-pull-requests/). - -Cobalt team members will be assigned to review your pull requests. A team -member will need to approve the workflow runs for each pull request. Once the -pull requests are approved and pass *all* presubmit checks, a Cobalt -team member will merge the pull request. - -### Contribution guidelines and standards - -Before sending your pull request for -[review](https://github.com/youtube/cobalt/pulls), -make sure your changes are consistent with the guidelines and follow the -Cobalt coding style. - -#### General guidelines and philosophy for contribution - -* Ensure your change references the bug you are addressing. Follow the - instructions - [here](https://cobalt.dev/communication.html#filing-bugs-and-feature-requests) - to view and create bugs. -* Include unit tests when you contribute new features, as they help to: - 1. Prove that your code works correctly - 1. Guard against future breaking changes to lower the maintenance cost. -* Bug fixes also generally require unit tests, because the presence of bugs - usually indicates insufficient test coverage. -* When you contribute a new feature to Cobalt, the maintenance burden is - (by default) transferred to the Cobalt team. This means that the benefit - of the contribution must be compared against the cost of maintaining the - feature. -* As every PR requires several CPU/GPU hours of CI testing, we discourage - submitting PRs to fix one typo, one warning,etc. We recommend fixing the - same issue at the file level at least (e.g.: fix all typos in a file, fix - all compiler warning in a file, etc.) - -#### License - -Include a license at the top of new files. Check existing files for license examples. - -#### Coding style - -Cobalt follows the -[Chromium style guide](https://chromium.googlesource.com/chromium/src/+/HEAD/styleguide/styleguide.md). - -Cobalt uses pre-commit to ensure good coding style. Create a python 3 virtual -environment for working with Cobalt, then install `pre-commit` with: - -```bash -$ pre-commit install -t post-checkout -t pre-commit -t pre-push --allow-missing-config -``` - -`pre-commit` will mostly run automatically, and can also be invoked manually. -You can find documentation about it at https://pre-commit.com/. - -#### Running unit tests - -First, ensure Docker and Docker Compose are installed on your system. Then, -you can run unit tests for our linux reference implementation using: - -```bash -$ docker compose up --build --no-start linux-x64x11-unittest -$ PLATFORM=linux-x64x11 CONFIG=devel TARGET=all docker compose run linux-x64x11 -$ PLATFORM=linux-x64x11 CONFIG=devel docker compose run linux-x64x11-unittest -```
diff --git a/README.md b/README.md index f29ca77..769afd2 100644 --- a/README.md +++ b/README.md
@@ -1,205 +1,7 @@ -# Cobalt [](https://github.com/youtube/cobalt/blob/main/BUILD_STATUS.md) +## Archival notice -[](https://codecov.io/github/youtube/cobalt) -[](https://github.com/youtube/cobalt/actions/workflows/lint.yaml?query=event%3Apush+branch%3Amain) -[](https://github.com/youtube/cobalt/actions/workflows/gradle.yaml?query=event%3Apush+branch%3Amain) -[](https://github.com/youtube/cobalt/actions/workflows/pytest.yaml?query=event%3Apush+branch%3Amain) \ -[](https://github.com/youtube/cobalt/actions/workflows/android.yaml?query=event%3Apush+branch%3Amain) -[](https://github.com/youtube/cobalt/actions/workflows/evergreen.yaml?query=event%3Apush+branch%3Amain) -[](https://github.com/youtube/cobalt/actions/workflows/linux.yaml?query=event%3Apush+branch%3Amain) -[](https://github.com/youtube/cobalt/actions/workflows/raspi-2.yaml?query=event%3Apush+branch%3Amain) -[](https://github.com/youtube/cobalt/actions/workflows/win32.yaml?query=event%3Apush+branch%3Amain) -[](https://github.com/youtube/cobalt/actions/workflows/xb1.yaml?query=event%3Apush+branch%3Amain) +This repository of Cobalt is no longer updated, and is kept for +reference only. -## Overview - -Cobalt is a lightweight application container (i.e. an application runtime, like -a JVM or the Flash Player) that is compatible with a subset of the W3C HTML5 -specifications. If you author a single-page web application (SPA) that complies -with the Cobalt Subset of W3C standards, it will run as well as possible on all -the devices that Cobalt supports. - - -## Motivation - -The Cobalt Authors originally maintained a port of Chromium called H5VCC, the -HTML5 Video Container for Consoles, ported to each of the major game consoles, -designed to run our HTML5-based video browse and play application. This took a -long time to port to each platform, consisted of 9 million lines of C++ code -(before we touched it), was dangerous to modify without unintended consequences, -and was thoroughly designed for a resource-rich, multi-process environment -(e.g. a desktop, laptop, or modern smartphone). - -After wrestling with this for several years, we imagined an environment that was -not designed for traditional scrolling web content, but was intended to be a -runtime environment for rich client applications built with the same -technologies -- HTML, CSS, JavaScript -- and designed from the ground-up to run -on constrained, embedded, Living Room Consumer Electronics (CE) devices, such as -Game Consoles, Set-Top Boxes (e.g. Cable, Satellite), OTT devices (e.g. Roku, -Apple TV, Chromecast, Fire TV), Blu-ray Disc Players, and Smart TVs. - -These constraints (not intended to be a canonical list) make this device -spectrum vastly different from the desktop computer environment targeted by -Chromium, FireFox, and IE: - - * **Limited Memory.** All except the very latest, expensive CE devices have a - very small amount of memory available for applications. This usually is - somewhere in the ballpark of 500MB, including graphics and media - memory, as opposed to multiple gigabytes of CPU memory (and more gigabytes - of GPU memory) in modern desktop and laptop computers, and mobile devices. - * **Slow CPUs.** Most CE devices have much slower CPUs than what is available - on even a budget desktop computer. Minor performance concerns can be greatly - exaggerated, which seriously affects priorities. Cobalt currently expects a - 4-core 32-bit ARMv7 CPU as a baseline. - * **Minimal GPU.** Not all CE devices have a monster GPU to throw shaders at - to offload CPU work. As CE devices now have a standard GPU (though not - nearly as powerful as even a laptop), OpenGL ES 2.0 is now required - by Cobalt. - * **Sometimes No JIT.** Many CE devices are dealing with "High-Value Content," - and, as such, are very sensitive to security concerns. Ensuring that - writable pages are not executable is a strong security protocol that can - prevent a wide spectrum of attacks. But, as a side effect, this also means - no ability to JIT. - * **Heterogeneous Development Environments.** This is slowly evening out, but - all CE devices run on custom hardware, often with proprietary methods of - building, packaging, deploying, and running programs. Sometimes the - toolchain doesn't support latest C++ language features. Sometimes the OS - does not support POSIX, or it is only partially implemented. - Sometimes the program entry point is in another language or architecture - that requires a "trampoline" over to native binary code. - * **No navigation.** The point of a Single-Page Application is that you don't - go through the HTTP page dance every time you switch screens. It's slow, and - provides poor user feedback, not to mention a jarring transition. Instead, - one loads data from an XMLHttpRequest (XHR), and then updates one's DOM to - reflect the new data. AJAX! Web 2.0!! - - -## Architecture - -The Cobalt Authors forked H5VCC, removed most of the Chromium code -- in -particular WebCore and the Chrome Renderer and Compositor -- and built up from -scratch an implementation of a simplified subset of HTML, the CSS Box Model for -layout, and the Web APIs that were really needed to build a full-screen SPA -browse and play application. - -The Cobalt technology stack has these major components, roughly in a high-level -application to a low-level platform order: - - * **Web Implementation** - This is where the W3C standards are implemented, - ultimately producing an annotated DOM tree that can be passed into the - Layout Engine to produce a Render Tree. Cobalt uses a forked copy of - [Chrome Blink's Web IDL compiler](https://www.chromium.org/blink/webidl/) to - turn JavaScript IDLs to generated C++ bindings code. - * **JavaScript Engine** - We have, perhaps surprisingly, *not* written our own - JavaScript Engine from scratch. Cobalt is running on [Chromiums V8](https://v8.dev) - JavaScript engine. V8 supports all of our target platforms, including very - restricted ones where write-and-execute memory pages (needed for JIT) are - not available. - * **Layout Engine** - The Layout Engine takes an annotated DOM Document - produced by the Web Implementation and JavaScript Engine working together, - and calculates a tree of rendering commands to send to the renderer (i.e. a - Render Tree). It caches intermediate layout artifacts so that subsequent - incremental layouts can be sped up. - * **Renderer/Skia** - The Renderer walks a Render Tree produced by the Layout - Engine, rasterizes it using the [Chromium graphics library Skia](https://skia.org/), and swaps - it to the front buffer. This is accomplished using Hardware Skia on OpenGL - ES 2.0. Note that the renderer runs in a different thread from the Layout - Engine, and can interpolate animations that do not require re-layout. This - decouples rendering from Layout and JavaScript, allowing for smooth, - consistent animations on platforms with a variety of capabilities. - * **Net / Media** - These are Chromium's Network and Media engines. We are - using them directly, as they don't cause any particular problems with the - extra constraints listed above. - * **Base** - This is Chromium's "Base" library, which contains a wide variety - of useful things used throughout Cobalt, Net, and Media. Cobalt uses a - combination of standard C++ containers (e.g. vector, string) and Base as the - foundation library for all of its code. - * **Other Third-party Libraries** - Most of these are venerable, straight-C, - open-source libraries that are commonly included in other open-source - software. Mostly format decoders and parsers (e.g. libpng, libxml2, - zlib). We fork these from Chromium, as we want them to be the most - battle-tested versions of these libraries. - * **Starboard** - **Starboard** is the Cobalt porting - interface. One major difference between Cobalt and Chromium is that we have - created a hard straight-C porting layer, and ported ALL of the compiled - code, including Base and all third-party libraries, to use it instead of - directly using POSIX standard libraries, which are not consistent, even on - modern systems (see Android, Windows, MacOS X, and iOS). Additionally, - Starboard includes APIs that haven't been effectively standardized across - platforms, such as display Window creation, Input events, and Media - playback. A good overview of which OS interfaces are abstracted by Starboard - can be found in the [reference documentation.](https://cobalt.dev/reference/starboard/modules/configuration.html) - * **ANGLE and Glimp** [**ANGLE** is a Chromium library](https://angleproject.org/) - that adapts OpenGL ES 2.0 graphics to various other platform-native graphics APIs. - Cobalt uses it on Windows platforms to run on DirectX. Glimp is a similar - custom adapter layer that translatest from GL ES2.0 to PlayStation native graphics. - -## The Cobalt Subset - -> Oh, we got both kinds of HTML tags,\ -> we got `<span>` and `<div>`! \ -> We even have CSS Flexbox now, hooray! - -See the [Cobalt Subset -specification](https://cobalt.dev/development/reference/supported-features.html) -for more details on which tags, properties, and Web APIs are supported in -Cobalt. - -## Interesting Source Locations - -All source locations are specified relative to `src/` (this directory). - - * `base/` - Chromium's Base library. Contains common utilities, and a light - platform abstraction, which has been superseded in Cobalt by Starboard. - * `net/` - Chromium's Network library. Contains enough infrastructure to - support the network needs of an HTTP User-Agent (like Chromium or Cobalt), - an HTTP server, a DIAL server, and several abstractions for networking - primitives. Also contains SPDY and QUIC implementations. - * `cobalt/` - The home of all Cobalt application code. This includes the Web - Implementation, Layout Engine, Renderer, and some other Cobalt-specific - features. - * `cobalt/build/` - The core build generation system, `gn.py`, and - configurations for supported platforms. - * `cobalt/doc/` - Contains a wide range of detailed information and guides - on Cobalt features, functionality and best practices for Cobalt - development. - * `cobalt/media/` - Chromium's Media library. Contains all the code that - parses, processes, and manages buffers of video and audio data. It - send the buffers to the SbPlayer implementation for playback. - * `starboard/` - Cobalt's porting layer. Please see Starboard's - [`README.md`](starboard/README.md) for more detailed information about - porting Starboard (and Cobalt) to a new platform. - * `third_party/` - Where all of Cobalt's third-party dependencies live. We - don't mean to be pejorative, we love our third-party libraries! This - location is dictated by Google OSS release management rules... - - -## Building and Running the Code - - See the below reference port setup guides for more details: - - * [Linux](cobalt/site/docs/development/setup-linux.md) - * [Raspi](cobalt/site/docs/development/setup-raspi.md) - * [Android](cobalt/site/docs/development/setup-android.md) - * [Docker](cobalt/site/docs/development/setup-docker.md) - * [RDK](cobalt/site/docs/development/setup-rdk.md) - -## Build Types - -Cobalt has four build optimization levels, going from the slowest, least -optimized, with the most debug information at the top (debug) to the fastest, -most optimized, and with the least debug information at the bottom (gold): - - Type | Optimizations | Logging | Asserts | Debug Info | Console - :---- | :------------ | :------ | :------ | :--------- | :------- - debug | None | Full | Full | Full | Enabled - devel | Full | Full | Full | Full | Enabled - qa | Full | Limited | None | None | Enabled - gold | Full | None | None | None | Disabled - -When building for release, you should always use a gold build for the final -product. - -## Origin of this Repository - -This is a fork of the chromium repository at http://git.chromium.org/git/chromium.git +Please use https://github.com/youtube/cobalt. Refer to https://cobalt.dev for +further details.
diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index e6b03a4..0000000 --- a/SECURITY.md +++ /dev/null
@@ -1,26 +0,0 @@ -# Security Policy - -## Supported Versions - -Currently supported stable Cobalt versions are. - -| Version | Release status | Supported | -| ------- | -----------------|------------------- | -| 25.lts | upcoming release | :white_check_mark: | -| 24.lts | last stable | :white_check_mark: | -| 23.lts | maintenance | :white_check_mark: | -| 22.lts | EOL | :no_entry: | -| < 21.lts| no support | :x: | - - -## Model - -Although Cobalt is built to process web content similar to browsers, -it is not a web browser. Our security model assumes _only trusted content -processing_. - -## Reporting a Vulnerability - -Please use [https://developers.google.com/youtube/cobalt](https://developers.google.com/youtube/cobalt/docs/communication) as an overall guide where to report issues. - -Critical security vulnerabilities should be reported via yt-cobalt-security@google.com.
diff --git a/base/BUILD.gn b/base/BUILD.gn deleted file mode 100644 index 5ad4e48..0000000 --- a/base/BUILD.gn +++ /dev/null
@@ -1,5142 +0,0 @@ -# Copyright 2013 The Chromium Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -# HOW TO WRITE CONDITIONALS IN THIS FILE -# ====================================== -# -# In many other places, one would write a conditional that expresses all the -# cases when a source file is used or unused, and then either add or subtract -# it from the sources list in that case -# -# Since base includes so many low-level things that vary widely and -# unpredictably for the various build types, we prefer a slightly different -# style. Instead, there are big per-platform blocks of inclusions and -# exclusions. If a given file has an inclusion or exclusion rule that applies -# for multiple conditions, prefer to duplicate it in both lists. This makes it -# a bit easier to see which files apply in which cases rather than having a -# huge sequence of random-looking conditionals. - -import("//base/allocator/allocator.gni") -import("//base/allocator/partition_allocator/partition_alloc.gni") -import("//base/debug/debug.gni") -import("//base/trace_event/tracing.gni") -import("//build/buildflag_header.gni") -import("//build/config/arm.gni") -import("//build/config/c++/c++.gni") -import("//build/config/chromecast_build.gni") -import("//build/config/chromeos/ui_mode.gni") -import("//build/config/compiler/compiler.gni") -import("//build/config/cronet/config.gni") -import("//build/config/dcheck_always_on.gni") -import("//build/config/ios/config.gni") -import("//build/config/logging.gni") -import("//build/config/nacl/config.gni") -import("//build/config/profiling/profiling.gni") -import("//build/config/rust.gni") -import("//build/config/sanitizers/sanitizers.gni") -import("//build/config/sysroot.gni") -import("//build/config/ui.gni") -import("//build/nocompile.gni") -import("//build/timestamp.gni") -import("//build_overrides/build.gni") -import("//testing/libfuzzer/fuzzer_test.gni") -import("//testing/test.gni") - -if (is_mac) { - # Used to generate fuzzer corpus :base_mach_port_rendezvous_convert_corpus. - import("//third_party/protobuf/proto_library.gni") -} - -if (is_apple) { - # Buildflags to control time behavior on iOS in file shared with mac. - import("//base/time/buildflags/buildflags.gni") -} - -if (is_win) { - import("//build/config/win/control_flow_guard.gni") -} - -declare_args() { - # Unsafe developer build. Has developer-friendly features that may weaken or - # disable security measures like sandboxing or ASLR. - # IMPORTANT: Unsafe developer builds should never be distributed to end users. - is_unsafe_developer_build = !is_official_build - - # Set to true to disable COM init check hooks. - com_init_check_hook_disabled = false - - # Set to true to enable mutex priority inheritance. See the comments in - # LockImpl::PriorityInheritanceAvailable() in lock_impl_posix.cc for the - # platform requirements to safely enable priority inheritance. - enable_mutex_priority_inheritance = false - - # Control whether the ios stack sampling profiler is enabled. This flag is - # only supported on iOS 64-bit architecture, but some project build //base - # for 32-bit architecture. - ios_stack_profiler_enabled = true -} - -# Mutex priority inheritance is disabled by default due to security -# vulnerabilities in older versions of Linux kernel and glibc. However, -# Chromecast builds have full control over the platform and ensure that -# the kernel and glibc versions used have patched the vulnerabilities, -# so it is safe to use mutex priority inheritance on Chromecast platform. -assert(!enable_mutex_priority_inheritance || is_castos || is_cast_android, - "Do not enable PI mutexes without consulting the security team") - -assert(!is_nacl || is_nacl_saigo, - "base must not be built in most nacl toolchains") - -# This is here instead of in //build because //build is DEPS'd in by a few -# subprojects that still support MSVC. -assert(!is_win || is_clang || use_cobalt_customizations, - "only clang-cl is supported on Windows, see https://crbug.com/988071") - -if (is_apple) { - assert(!use_blink || enable_mach_absolute_time_ticks, - "use_blink requires mach absolute time ticks") - - assert(!is_mac || enable_mach_absolute_time_ticks, - "mac requires mach absolute time ticks") -} - -# Determines whether libevent should be dep. -dep_libevent = !is_fuchsia && !is_win && !is_mac && !is_nacl - && !use_cobalt_customizations - -# Determines whether message_pump_libevent should be used. -use_libevent = dep_libevent && !is_ios - -if (is_android || is_robolectric) { - import("//build/config/android/rules.gni") -} - -if (is_fuchsia) { - import("//third_party/fuchsia-sdk/sdk/build/fidl_library.gni") -} - -# TODO(crbug.com/1280708): Drop toolchain_has_rust and move the C++ parser into -# components/nacl to just run in-process there. Don't compile base::JSONReader -# on NaCL at all. -# -# TODO(crbug.com/1304707): Drop toolchain_has_rust after we have support for all -# our toolchains: Linux x86 is missing in order to build for Android. -# -# Rust to C++ type conversions. -build_rust_base_conversions = toolchain_has_rust && enable_rust_base_conversions - -# The Rust implementation of base::JSONReader. -build_rust_json_reader = toolchain_has_rust && enable_rust_json - -assert(build_rust_base_conversions || !build_rust_json_reader, - "Cannot enable Rust JSON decoder without also base conversions") - -buildflag_header("rust_buildflags") { - header = "rust_buildflags.h" - flags = [ - "BUILD_RUST_JSON_READER=$build_rust_json_reader", - "BUILD_RUST_BASE_CONVERSIONS=$build_rust_base_conversions", - ] -} - -if (is_win) { - # This is in a separate config so the flags can be applied to dependents. - # ldflags in GN aren't automatically inherited. - config("base_win_linker_flags") { - ldflags = [ - # Linking with shcore.lib causes the shcore api forwarder dll to load. - "/DELAYLOAD:api-ms-win-shcore-scaling-l1-1-1.dll", - - # Linking with OneCore.lib causes the next two dlls to load. - "/DELAYLOAD:api-ms-win-power-base-l1-1-0.dll", - "/DELAYLOAD:api-ms-win-power-setting-l1-1-0.dll", - "/DELAYLOAD:cfgmgr32.dll", - "/DELAYLOAD:powrprof.dll", - "/DELAYLOAD:setupapi.dll", - "/DELAYLOAD:shcore.dll", - ] - } -} - -if (is_android) { - config("android_system_libs") { - libs = [ - "android", - "log", # Used by logging.cc. - ] - } -} - -if (is_fuchsia) { - config("fuchsia_sync_lib") { - libs = [ - "sync", # Used by spinning_mutex.h. - ] - } -} - -config("perfetto_config") { - if (use_perfetto_client_library) { - defines = [ - # Use TRACE_EVENT macro implementation from Perfetto. See - # trace_event/trace_event_common.h. - "BASE_USE_PERFETTO_CLIENT_LIBRARY=1", - ] - } -} - -if (enable_pkeys && is_debug) { - config("no_stack_protector") { - cflags = [ "-fno-stack-protector" ] - } -} - -if (is_starboard) { - config("starboard_config") { - if (!is_gold) { - defines = [ "ENABLE_TEST_DATA" ] - } - } -} - -buildflag_header("ios_cronet_buildflags") { - header = "ios_cronet_buildflags.h" - header_dir = "base/message_loop" - flags = [ "CRONET_BUILD=$is_cronet_build" ] -} - -enable_message_pump_epoll = - !use_cobalt_customizations && (is_linux || is_chromeos || is_android) -buildflag_header("message_pump_buildflags") { - header = "message_pump_buildflags.h" - header_dir = "base/message_loop" - flags = [ "ENABLE_MESSAGE_PUMP_EPOLL=$enable_message_pump_epoll" ] -} - -# Base and everything it depends on should be a static library rather than -# a source set. Base is more of a "library" in the classic sense in that many -# small parts of it are used in many different contexts. This combined with a -# few static initializers floating around means that dead code stripping -# still leaves a lot of code behind that isn't always used. For example, this -# saves more than 40K for a smaller target like chrome_elf. -# -# Use static libraries for the helper stuff as well for things like -# //base/debug since those targets refer back to base code, which will force -# base compilation units to be linked in where they wouldn't have otherwise. -# This does not include test code (test support and anything in the test -# directory) which should use source_set as is recommended for GN targets). -component("base") { - sources = [ - "allocator/allocator_check.cc", - "allocator/allocator_check.h", - "allocator/allocator_extension.cc", - "allocator/allocator_extension.h", - "allocator/dispatcher/configuration.h", - "allocator/dispatcher/dispatcher.cc", - "allocator/dispatcher/dispatcher.h", - "allocator/dispatcher/initializer.h", - "allocator/dispatcher/internal/dispatch_data.cc", - "allocator/dispatcher/internal/dispatch_data.h", - "allocator/dispatcher/internal/dispatcher_internal.h", - "allocator/dispatcher/internal/tools.h", - "allocator/dispatcher/reentry_guard.cc", - "allocator/dispatcher/reentry_guard.h", - "allocator/dispatcher/subsystem.h", - "allocator/dispatcher/tls.cc", - "allocator/dispatcher/tls.h", - "at_exit.cc", - "at_exit.h", - "atomic_ref_count.h", - "atomic_sequence_num.h", - "atomicops.h", - "atomicops_internals_atomicword_compat.h", - "atomicops_internals_portable.h", - "auto_reset.h", - "barrier_callback.h", - "barrier_closure.cc", - "barrier_closure.h", - "base64.cc", - "base64.h", - "base64url.cc", - "base64url.h", - "base_switches.h", - "big_endian.cc", - "big_endian.h", - "bit_cast.h", - "bits.h", - "build_time.h", - "callback_list.cc", - "callback_list.h", - "cancelable_callback.h", - "check.cc", - "check.h", - "check_deref.h", - "check_is_test.cc", - "check_is_test.h", - "check_op.cc", - "check_op.h", - "command_line.cc", - "command_line.h", - "compiler_specific.h", - "component_export.h", - "containers/adapters.h", - "containers/buffer_iterator.h", - "containers/checked_iterators.h", - "containers/circular_deque.h", - "containers/contains.h", - "containers/contiguous_iterator.h", - "containers/cxx20_erase.h", - "containers/cxx20_erase_deque.h", - "containers/cxx20_erase_forward_list.h", - "containers/cxx20_erase_internal.h", - "containers/cxx20_erase_list.h", - "containers/cxx20_erase_map.h", - "containers/cxx20_erase_set.h", - "containers/cxx20_erase_string.h", - "containers/cxx20_erase_unordered_map.h", - "containers/cxx20_erase_unordered_set.h", - "containers/cxx20_erase_vector.h", - "containers/enum_set.h", - "containers/extend.h", - "containers/fixed_flat_map.h", - "containers/fixed_flat_set.h", - "containers/flat_map.h", - "containers/flat_set.h", - "containers/flat_tree.cc", - "containers/flat_tree.h", - "containers/id_map.h", - "containers/intrusive_heap.cc", - "containers/intrusive_heap.h", - "containers/linked_list.cc", - "containers/linked_list.h", - "containers/lru_cache.h", - "containers/small_map.h", - "containers/span.h", - "containers/span_rust.h", - "containers/stack.h", - "containers/stack_container.h", - "containers/unique_ptr_adapters.h", - "containers/util.h", - "containers/vector_buffer.h", - "cpu_reduction_experiment.cc", - "cpu_reduction_experiment.h", - "critical_closure.h", - "cxx17_backports.h", - "cxx20_is_constant_evaluated.h", - "cxx20_to_address.h", - "dcheck_is_on.h", - "debug/alias.cc", - "debug/alias.h", - "debug/asan_invalid_access.cc", - "debug/asan_invalid_access.h", - "debug/asan_service.cc", - "debug/asan_service.h", - "debug/buffered_dwarf_reader.cc", - "debug/buffered_dwarf_reader.h", - "debug/debugger.cc", - "debug/debugger.h", - "debug/dump_without_crashing.cc", - "debug/dump_without_crashing.h", - "debug/dwarf_line_no.cc", - "debug/dwarf_line_no.h", - "debug/leak_annotations.h", - "debug/profiler.cc", - "debug/profiler.h", - "debug/task_trace.cc", - "debug/task_trace.h", - "environment.cc", - "environment.h", - "export_template.h", - "feature_list.cc", - "feature_list.h", - "features.cc", - "features.h", - "file_version_info.h", - "files/dir_reader_fallback.h", - "files/file.cc", - "files/file.h", - "files/file_error_or.h", - "files/file_path.cc", - "files/file_path.h", - "files/file_tracing.cc", - "files/file_tracing.h", - "files/memory_mapped_file.cc", - "files/memory_mapped_file.h", - "files/platform_file.h", - "files/safe_base_name.cc", - "files/safe_base_name.h", - "files/scoped_file.cc", - "files/scoped_file.h", - "format_macros.h", - "functional/bind.h", - "functional/bind_internal.h", - "functional/callback.h", - "functional/callback_forward.h", - "functional/callback_helpers.cc", - "functional/callback_helpers.h", - "functional/callback_internal.cc", - "functional/callback_internal.h", - "functional/callback_tags.h", - "functional/disallow_unretained.h", - "functional/function_ref.h", - "functional/identity.h", - "functional/invoke.h", - "functional/not_fn.h", - "functional/overloaded.h", - "functional/unretained_traits.h", - "gtest_prod_util.h", - "guid.h", - "hash/hash.cc", - "hash/hash.h", - "hash/legacy_hash.cc", - "hash/legacy_hash.h", - "json/json_common.h", - "json/json_parser.cc", - "json/json_parser.h", - "json/json_reader.cc", - "json/json_reader.h", - "json/json_string_value_serializer.cc", - "json/json_string_value_serializer.h", - "json/json_value_converter.cc", - "json/json_value_converter.h", - "json/json_writer.cc", - "json/json_writer.h", - "json/string_escape.cc", - "json/string_escape.h", - "json/values_util.cc", - "json/values_util.h", - "lazy_instance.h", - "lazy_instance_helpers.cc", - "lazy_instance_helpers.h", - "linux_util.cc", - "linux_util.h", - "location.cc", - "location.h", - "logging.cc", - "logging.h", - "memory/aligned_memory.cc", - "memory/aligned_memory.h", - "memory/free_deleter.h", - "memory/memory_pressure_listener.cc", - "memory/memory_pressure_listener.h", - "memory/memory_pressure_monitor.cc", - "memory/memory_pressure_monitor.h", - "memory/nonscannable_memory.cc", - "memory/nonscannable_memory.h", - "memory/page_size.h", - "memory/platform_shared_memory_handle.cc", - "memory/platform_shared_memory_handle.h", - "memory/platform_shared_memory_mapper.h", - "memory/platform_shared_memory_region.cc", - "memory/platform_shared_memory_region.h", - "memory/ptr_util.h", - "memory/raw_ptr.h", - "memory/raw_ptr_asan_bound_arg_tracker.cc", - "memory/raw_ptr_asan_bound_arg_tracker.h", - "memory/raw_ptr_asan_hooks.cc", - "memory/raw_ptr_asan_hooks.h", - "memory/raw_ptr_asan_service.cc", - "memory/raw_ptr_asan_service.h", - "memory/raw_ptr_exclusion.h", - "memory/raw_ref.h", - "memory/raw_scoped_refptr_mismatch_checker.h", - "memory/read_only_shared_memory_region.cc", - "memory/read_only_shared_memory_region.h", - "memory/ref_counted.cc", - "memory/ref_counted.h", - "memory/ref_counted_delete_on_sequence.h", - "memory/ref_counted_memory.cc", - "memory/ref_counted_memory.h", - "memory/safe_ref.h", - "memory/scoped_policy.h", - "memory/scoped_refptr.h", - "memory/shared_memory_hooks.h", - "memory/shared_memory_mapper.cc", - "memory/shared_memory_mapper.h", - "memory/shared_memory_mapping.cc", - "memory/shared_memory_mapping.h", - "memory/shared_memory_security_policy.cc", - "memory/shared_memory_security_policy.h", - "memory/shared_memory_tracker.cc", - "memory/shared_memory_tracker.h", - "memory/singleton.h", - "memory/stack_allocated.h", - "memory/unsafe_shared_memory_pool.cc", - "memory/unsafe_shared_memory_pool.h", - "memory/unsafe_shared_memory_region.cc", - "memory/unsafe_shared_memory_region.h", - "memory/values_equivalent.h", - "memory/weak_auto_reset.h", - "memory/weak_ptr.cc", - "memory/weak_ptr.h", - "memory/writable_shared_memory_region.cc", - "memory/writable_shared_memory_region.h", - "message_loop/message_pump.cc", - "message_loop/message_pump.h", - "message_loop/message_pump_default.cc", - "message_loop/message_pump_default.h", - "message_loop/message_pump_for_io.h", - "message_loop/message_pump_for_ui.h", - "message_loop/message_pump_type.h", - "message_loop/timer_slack.h", - "message_loop/work_id_provider.cc", - "message_loop/work_id_provider.h", - "metrics/bucket_ranges.cc", - "metrics/bucket_ranges.h", - "metrics/crc32.cc", - "metrics/crc32.h", - "metrics/dummy_histogram.cc", - "metrics/dummy_histogram.h", - "metrics/field_trial.cc", - "metrics/field_trial.h", - "metrics/field_trial_list_including_low_anonymity.cc", - "metrics/field_trial_list_including_low_anonymity.h", - "metrics/field_trial_param_associator.cc", - "metrics/field_trial_param_associator.h", - "metrics/field_trial_params.cc", - "metrics/field_trial_params.h", - "metrics/histogram.cc", - "metrics/histogram.h", - "metrics/histogram_base.cc", - "metrics/histogram_base.h", - "metrics/histogram_delta_serialization.cc", - "metrics/histogram_delta_serialization.h", - "metrics/histogram_flattener.h", - "metrics/histogram_functions.cc", - "metrics/histogram_functions.h", - "metrics/histogram_macros.h", - "metrics/histogram_macros_internal.h", - "metrics/histogram_macros_local.h", - "metrics/histogram_samples.cc", - "metrics/histogram_samples.h", - "metrics/histogram_snapshot_manager.cc", - "metrics/histogram_snapshot_manager.h", - "metrics/metrics_hashes.cc", - "metrics/metrics_hashes.h", - "metrics/persistent_histogram_allocator.cc", - "metrics/persistent_histogram_allocator.h", - "metrics/persistent_memory_allocator.cc", - "metrics/persistent_memory_allocator.h", - "metrics/persistent_sample_map.cc", - "metrics/persistent_sample_map.h", - "metrics/ranges_manager.cc", - "metrics/ranges_manager.h", - "metrics/record_histogram_checker.h", - "metrics/sample_map.cc", - "metrics/sample_map.h", - "metrics/sample_vector.cc", - "metrics/sample_vector.h", - "metrics/single_sample_metrics.cc", - "metrics/single_sample_metrics.h", - "metrics/sparse_histogram.cc", - "metrics/sparse_histogram.h", - "metrics/statistics_recorder.cc", - "metrics/statistics_recorder.h", - "metrics/user_metrics.cc", - "metrics/user_metrics.h", - "metrics/user_metrics_action.h", - "no_destructor.h", - "notreached.h", - "observer_list.h", - "observer_list_internal.cc", - "observer_list_internal.h", - "observer_list_threadsafe.cc", - "observer_list_threadsafe.h", - "observer_list_types.cc", - "observer_list_types.h", - "one_shot_event.cc", - "one_shot_event.h", - "parameter_pack.h", - "pending_task.cc", - "pending_task.h", - "pickle.cc", - "pickle.h", - "power_monitor/battery_level_provider.cc", - "power_monitor/battery_level_provider.h", - "power_monitor/battery_state_sampler.cc", - "power_monitor/battery_state_sampler.h", - "power_monitor/moving_average.cc", - "power_monitor/moving_average.h", - "power_monitor/power_monitor.cc", - "power_monitor/power_monitor.h", - "power_monitor/power_monitor_device_source.cc", - "power_monitor/power_monitor_device_source.h", - "power_monitor/power_monitor_features.cc", - "power_monitor/power_monitor_features.h", - "power_monitor/power_monitor_source.cc", - "power_monitor/power_monitor_source.h", - "power_monitor/power_observer.h", - "power_monitor/sampling_event_source.cc", - "power_monitor/sampling_event_source.h", - "power_monitor/timer_sampling_event_source.cc", - "power_monitor/timer_sampling_event_source.h", - "process/current_process.cc", - "process/current_process.h", - "process/current_process_test.h", - "process/environment_internal.cc", - "process/environment_internal.h", - "process/process.h", - "process/process_handle.cc", - "process/process_handle.h", - "process/process_info.h", - "process/process_metrics_iocounters.h", - "profiler/frame.cc", - "profiler/frame.h", - "profiler/metadata_recorder.cc", - "profiler/metadata_recorder.h", - "profiler/module_cache.cc", - "profiler/module_cache.h", - "profiler/profile_builder.h", - "profiler/register_context.h", - "profiler/sample_metadata.cc", - "profiler/sample_metadata.h", - "profiler/sampling_profiler_thread_token.cc", - "profiler/sampling_profiler_thread_token.h", - "profiler/stack_buffer.cc", - "profiler/stack_buffer.h", - "profiler/stack_copier.cc", - "profiler/stack_copier.h", - "profiler/stack_copier_suspend.cc", - "profiler/stack_copier_suspend.h", - "profiler/stack_sampler.cc", - "profiler/stack_sampler.h", - "profiler/stack_sampling_profiler.cc", - "profiler/stack_sampling_profiler.h", - "profiler/suspendable_thread_delegate.h", - "profiler/thread_delegate.h", - "profiler/unwinder.cc", - "profiler/unwinder.h", - "rand_util.cc", - "rand_util.h", - "ranges/algorithm.h", - "ranges/functional.h", - "ranges/ranges.h", - "run_loop.cc", - "run_loop.h", - "sampling_heap_profiler/lock_free_address_hash_set.cc", - "sampling_heap_profiler/lock_free_address_hash_set.h", - "sampling_heap_profiler/poisson_allocation_sampler.cc", - "sampling_heap_profiler/poisson_allocation_sampler.h", - "sampling_heap_profiler/sampling_heap_profiler.cc", - "sampling_heap_profiler/sampling_heap_profiler.h", - "scoped_add_feature_flags.cc", - "scoped_add_feature_flags.h", - "scoped_clear_last_error.h", - "scoped_environment_variable_override.cc", - "scoped_environment_variable_override.h", - "scoped_generic.h", - "scoped_multi_source_observation.h", - "scoped_observation.h", - "scoped_observation_traits.h", - "scoped_observation_traits_internal.h", - "sequence_checker.cc", - "sequence_checker.h", - "sequence_checker_impl.cc", - "sequence_checker_impl.h", - "sequence_token.cc", - "sequence_token.h", - "state_transitions.h", - "stl_util.h", - "strings/abseil_string_number_conversions.cc", - "strings/abseil_string_number_conversions.h", - "strings/escape.cc", - "strings/escape.h", - "strings/latin1_string_conversions.cc", - "strings/latin1_string_conversions.h", - "strings/pattern.cc", - "strings/pattern.h", - "strings/safe_sprintf.cc", - "strings/safe_sprintf.h", - "strings/strcat.cc", - "strings/strcat.h", - "strings/strcat_internal.h", - "strings/string_number_conversions.cc", - "strings/string_number_conversions.h", - "strings/string_number_conversions_internal.h", - "strings/string_piece.cc", - "strings/string_piece.h", - "strings/string_piece_forward.h", - "strings/string_piece_rust.h", - "strings/string_split.cc", - "strings/string_split.h", - "strings/string_split_internal.h", - "strings/string_tokenizer.h", - "strings/string_util.cc", - "strings/string_util.h", - "strings/string_util_constants.cc", - "strings/string_util_impl_helpers.h", - "strings/string_util_internal.h", - "strings/stringize_macros.h", - "strings/stringprintf.cc", - "strings/stringprintf.h", - "strings/sys_string_conversions.h", - "strings/to_string.h", - "strings/utf_offset_string_conversions.cc", - "strings/utf_offset_string_conversions.h", - "strings/utf_string_conversion_utils.cc", - "strings/utf_string_conversion_utils.h", - "strings/utf_string_conversions.cc", - "strings/utf_string_conversions.h", - "substring_set_matcher/matcher_string_pattern.cc", - "substring_set_matcher/matcher_string_pattern.h", - "substring_set_matcher/substring_set_matcher.cc", - "substring_set_matcher/substring_set_matcher.h", - "supports_user_data.cc", - "supports_user_data.h", - "synchronization/atomic_flag.cc", - "synchronization/atomic_flag.h", - "synchronization/condition_variable.h", - "synchronization/lock.cc", - "synchronization/lock.h", - "synchronization/lock_impl.h", - "synchronization/waitable_event.cc", - "synchronization/waitable_event.h", - "synchronization/waitable_event_watcher.h", - "sys_byteorder.h", - "syslog_logging.cc", - "syslog_logging.h", - "system/system_monitor.cc", - "system/system_monitor.h", - "task/bind_post_task.h", - "task/bind_post_task_internal.h", - "task/cancelable_task_tracker.cc", - "task/cancelable_task_tracker.h", - "task/common/checked_lock.h", - "task/common/checked_lock_impl.cc", - "task/common/checked_lock_impl.h", - "task/common/lazy_now.cc", - "task/common/lazy_now.h", - "task/common/operations_controller.cc", - "task/common/operations_controller.h", - "task/common/scoped_defer_task_posting.cc", - "task/common/scoped_defer_task_posting.h", - "task/common/task_annotator.cc", - "task/common/task_annotator.h", - "task/current_thread.cc", - "task/current_thread.h", - "task/default_delayed_task_handle_delegate.cc", - "task/default_delayed_task_handle_delegate.h", - "task/deferred_sequenced_task_runner.cc", - "task/deferred_sequenced_task_runner.h", - "task/delay_policy.h", - "task/delayed_task_handle.cc", - "task/delayed_task_handle.h", - "task/lazy_thread_pool_task_runner.cc", - "task/lazy_thread_pool_task_runner.h", - "task/post_job.cc", - "task/post_job.h", - "task/post_task_and_reply_with_result_internal.h", - "task/scoped_set_task_priority_for_current_thread.cc", - "task/scoped_set_task_priority_for_current_thread.h", - "task/sequence_manager/associated_thread_id.cc", - "task/sequence_manager/associated_thread_id.h", - "task/sequence_manager/atomic_flag_set.cc", - "task/sequence_manager/atomic_flag_set.h", - "task/sequence_manager/delayed_task_handle_delegate.cc", - "task/sequence_manager/delayed_task_handle_delegate.h", - "task/sequence_manager/enqueue_order.h", - "task/sequence_manager/enqueue_order_generator.cc", - "task/sequence_manager/enqueue_order_generator.h", - "task/sequence_manager/fence.cc", - "task/sequence_manager/fence.h", - "task/sequence_manager/hierarchical_timing_wheel.cc", - "task/sequence_manager/hierarchical_timing_wheel.h", - "task/sequence_manager/lazily_deallocated_deque.h", - "task/sequence_manager/sequence_manager.cc", - "task/sequence_manager/sequence_manager.h", - "task/sequence_manager/sequence_manager_impl.cc", - "task/sequence_manager/sequence_manager_impl.h", - "task/sequence_manager/sequenced_task_source.cc", - "task/sequence_manager/sequenced_task_source.h", - "task/sequence_manager/task_order.cc", - "task/sequence_manager/task_order.h", - "task/sequence_manager/task_queue.cc", - "task/sequence_manager/task_queue.h", - "task/sequence_manager/task_queue_impl.cc", - "task/sequence_manager/task_queue_impl.h", - "task/sequence_manager/task_queue_selector.cc", - "task/sequence_manager/task_queue_selector.h", - "task/sequence_manager/task_time_observer.h", - "task/sequence_manager/tasks.cc", - "task/sequence_manager/tasks.h", - "task/sequence_manager/thread_controller.cc", - "task/sequence_manager/thread_controller.h", - "task/sequence_manager/thread_controller_impl.cc", - "task/sequence_manager/thread_controller_impl.h", - "task/sequence_manager/thread_controller_power_monitor.cc", - "task/sequence_manager/thread_controller_power_monitor.h", - "task/sequence_manager/thread_controller_with_message_pump_impl.cc", - "task/sequence_manager/thread_controller_with_message_pump_impl.h", - "task/sequence_manager/time_domain.cc", - "task/sequence_manager/time_domain.h", - "task/sequence_manager/timing_wheel.cc", - "task/sequence_manager/timing_wheel.h", - "task/sequence_manager/wake_up_queue.cc", - "task/sequence_manager/wake_up_queue.h", - "task/sequence_manager/work_deduplicator.cc", - "task/sequence_manager/work_deduplicator.h", - "task/sequence_manager/work_queue.cc", - "task/sequence_manager/work_queue.h", - "task/sequence_manager/work_queue_sets.cc", - "task/sequence_manager/work_queue_sets.h", - "task/sequenced_task_runner.cc", - "task/sequenced_task_runner.h", - "task/sequenced_task_runner_helpers.h", - "task/single_thread_task_executor.cc", - "task/single_thread_task_executor.h", - "task/single_thread_task_runner.cc", - "task/single_thread_task_runner.h", - "task/single_thread_task_runner_thread_mode.h", - "task/task_features.cc", - "task/task_features.h", - "task/task_observer.h", - "task/task_runner.cc", - "task/task_runner.h", - "task/task_traits.cc", - "task/task_traits.h", - "task/thread_pool.cc", - "task/thread_pool.h", - "task/thread_pool/delayed_priority_queue.cc", - "task/thread_pool/delayed_priority_queue.h", - "task/thread_pool/delayed_task_manager.cc", - "task/thread_pool/delayed_task_manager.h", - "task/thread_pool/environment_config.cc", - "task/thread_pool/environment_config.h", - "task/thread_pool/job_task_source.cc", - "task/thread_pool/job_task_source.h", - "task/thread_pool/pooled_parallel_task_runner.cc", - "task/thread_pool/pooled_parallel_task_runner.h", - "task/thread_pool/pooled_sequenced_task_runner.cc", - "task/thread_pool/pooled_sequenced_task_runner.h", - "task/thread_pool/pooled_single_thread_task_runner_manager.cc", - "task/thread_pool/pooled_single_thread_task_runner_manager.h", - "task/thread_pool/pooled_task_runner_delegate.cc", - "task/thread_pool/pooled_task_runner_delegate.h", - "task/thread_pool/priority_queue.cc", - "task/thread_pool/priority_queue.h", - "task/thread_pool/sequence.cc", - "task/thread_pool/sequence.h", - "task/thread_pool/service_thread.cc", - "task/thread_pool/service_thread.h", - "task/thread_pool/task.cc", - "task/thread_pool/task.h", - "task/thread_pool/task_source.cc", - "task/thread_pool/task_source.h", - "task/thread_pool/task_source_sort_key.cc", - "task/thread_pool/task_source_sort_key.h", - "task/thread_pool/task_tracker.cc", - "task/thread_pool/task_tracker.h", - "task/thread_pool/thread_group.cc", - "task/thread_pool/thread_group.h", - "task/thread_pool/thread_group_impl.cc", - "task/thread_pool/thread_group_impl.h", - "task/thread_pool/thread_pool_impl.cc", - "task/thread_pool/thread_pool_impl.h", - "task/thread_pool/thread_pool_instance.cc", - "task/thread_pool/thread_pool_instance.h", - "task/thread_pool/tracked_ref.h", - "task/thread_pool/worker_thread.cc", - "task/thread_pool/worker_thread.h", - "task/thread_pool/worker_thread_observer.h", - "task/thread_pool/worker_thread_set.cc", - "task/thread_pool/worker_thread_set.h", - "task/updateable_sequenced_task_runner.h", - "template_util.h", - "test/malloc_wrapper.h", - "test/scoped_logging_settings.h", - "test/spin_wait.h", - "third_party/cityhash/city.cc", - "third_party/cityhash/city.h", - "third_party/cityhash_v103/src/city_v103.cc", - "third_party/cityhash_v103/src/city_v103.h", - "third_party/icu/icu_utf.h", - "third_party/nspr/prtime.cc", - "third_party/nspr/prtime.h", - "third_party/superfasthash/superfasthash.c", - "thread_annotations.h", - "threading/hang_watcher.cc", - "threading/hang_watcher.h", - "threading/platform_thread.cc", - "threading/platform_thread.h", - "threading/platform_thread_ref.cc", - "threading/platform_thread_ref.h", - "threading/post_task_and_reply_impl.cc", - "threading/post_task_and_reply_impl.h", - "threading/scoped_blocking_call.cc", - "threading/scoped_blocking_call.h", - "threading/scoped_blocking_call_internal.cc", - "threading/scoped_blocking_call_internal.h", - "threading/scoped_thread_priority.cc", - "threading/scoped_thread_priority.h", - "threading/sequence_bound.h", - "threading/sequence_bound_internal.h", - "threading/sequence_local_storage_map.cc", - "threading/sequence_local_storage_map.h", - "threading/sequence_local_storage_slot.cc", - "threading/sequence_local_storage_slot.h", - "threading/simple_thread.cc", - "threading/simple_thread.h", - "threading/thread.cc", - "threading/thread.h", - "threading/thread_checker.cc", - "threading/thread_checker.h", - "threading/thread_checker_impl.cc", - "threading/thread_checker_impl.h", - "threading/thread_collision_warner.cc", - "threading/thread_collision_warner.h", - "threading/thread_id_name_manager.cc", - "threading/thread_id_name_manager.h", - "threading/thread_local.h", - "threading/thread_local_internal.h", - "threading/thread_local_storage.cc", - "threading/thread_local_storage.h", - "threading/thread_restrictions.cc", - "threading/thread_restrictions.h", - "threading/threading_features.h", - "threading/watchdog.cc", - "threading/watchdog.h", - "time/clock.cc", - "time/clock.h", - "time/default_clock.cc", - "time/default_clock.h", - "time/default_tick_clock.cc", - "time/default_tick_clock.h", - "time/tick_clock.cc", - "time/tick_clock.h", - "time/time.cc", - "time/time.h", - "time/time_delta_from_string.cc", - "time/time_delta_from_string.h", - "time/time_override.cc", - "time/time_override.h", - "time/time_to_iso8601.cc", - "time/time_to_iso8601.h", - "timer/elapsed_timer.cc", - "timer/elapsed_timer.h", - "timer/hi_res_timer_manager.h", - "timer/lap_timer.cc", - "timer/lap_timer.h", - "timer/timer.cc", - "timer/timer.h", - "timer/wall_clock_timer.cc", - "timer/wall_clock_timer.h", - "token.cc", - "token.h", - "trace_event/base_tracing.h", - "trace_event/base_tracing_forward.h", - "trace_event/common/trace_event_common.h", - "trace_event/heap_profiler_allocation_context.cc", - "trace_event/heap_profiler_allocation_context.h", - "trace_event/heap_profiler_allocation_context_tracker.cc", - "trace_event/heap_profiler_allocation_context_tracker.h", - "trace_event/memory_allocator_dump_guid.cc", - "trace_event/memory_allocator_dump_guid.h", - "trace_event/trace_id_helper.cc", - "trace_event/trace_id_helper.h", - "traits_bag.h", - "tuple.h", - "types/always_false.h", - "types/cxx23_to_underlying.h", - "types/expected.h", - "types/expected_internal.h", - "types/id_type.h", - "types/optional_ref.h", - "types/optional_util.h", - "types/pass_key.h", - "types/strong_alias.h", - "types/supports_ostream_operator.h", - "types/token_type.h", - "types/variant_util.h", - "unguessable_token.cc", - "unguessable_token.h", - "uuid.cc", - "uuid.h", - "value_iterators.cc", - "value_iterators.h", - "values.cc", - "values.h", - "version.cc", - "version.h", - "vlog.cc", - "vlog.h", - ] - - if (use_cobalt_customizations && current_toolchain == default_toolchain) { - sources += [ - "base_paths_starboard.cc", - "base_paths_starboard.h", - "debug/debugger_starboard.cc", - "debug/stack_trace_starboard.cc", - "files/file_enumerator_starboard.cc", - "files/file_path_watcher_stub.cc", - "files/file_starboard.cc", - "files/file_util_starboard.cc", - "files/memory_mapped_file_starboard.cc", - "memory/page_size_starboard.cc", - "memory/platform_shared_memory_mapper_starboard.cc", - "memory/platform_shared_memory_region_starboard.cc", - "message_loop/message_pump_io_starboard.cc", - "message_loop/message_pump_io_starboard.h", - "message_loop/message_pump_ui_starboard.cc", - "message_loop/message_pump_ui_starboard.h", - "process/launch.cc", - "process/launch.h", - "process/launch_starboard.cc", - "process/memory_starboard.cc", - "process/process_starboard.cc", - "profiler/module_cache_starboard.cc", - "profiler/stack_sampler_starboard.cc", - "rand_util_starboard.cc", - "strings/string_util_starboard.cc", - "strings/string_util_starboard.h", - "strings/sys_string_conversions_starboard.cc", - "synchronization/condition_variable_starboard.cc", - "synchronization/lock_impl_starboard.cc", - "synchronization/waitable_event_starboard.cc", - "synchronization/waitable_event_watcher_starboard.cc", - "sys_info_starboard.cc", - "threading/platform_thread_starboard.cc", - "threading/thread_local_storage_starboard.cc", - "threading/thread_task_runner_handle.cc", - "time/time_now_starboard.cc", - "time/time_starboard.cc", - ] - } - - # Various files that are unused in the Chromium build, but presumably here to - # make downstream's life easier. They are not included in the main sources - # list to avoid breaking GN formatting's auto-sorting. - sources += [ - #"process/process_handle_freebsd.cc", - #"process/process_iterator_freebsd.cc", - #"process/process_metrics_freebsd.cc", - #"system/sys_info_freebsd.cc", - #"process/process_iterator_openbsd.cc", - #"process/process_handle_openbsd.cc", - #"process/process_metrics_openbsd.cc", - #"system/sys_info_openbsd.cc", - ] - - if (!use_cobalt_customizations && is_posix) { - sources += [ - "debug/debugger_posix.cc", - "file_descriptor_posix.cc", - "file_descriptor_posix.h", - "files/dir_reader_posix.h", - "files/file_posix.cc", - "files/memory_mapped_file_posix.cc", - "message_loop/watchable_io_message_pump_posix.cc", - "message_loop/watchable_io_message_pump_posix.h", - "posix/eintr_wrapper.h", - "posix/file_descriptor_shuffle.cc", - "posix/file_descriptor_shuffle.h", - "posix/global_descriptors.cc", - "posix/global_descriptors.h", - "posix/safe_strerror.cc", - "posix/safe_strerror.h", - "process/process_handle_posix.cc", - "strings/string_util_posix.h", - "synchronization/condition_variable_posix.cc", - "synchronization/lock_impl_posix.cc", - "threading/platform_thread_posix.cc", - "threading/thread_local_storage_posix.cc", - "time/time_conversion_posix.cc", - "timer/hi_res_timer_manager_posix.cc", - ] - } - - if (!use_cobalt_customizations && (is_linux || is_chromeos)) { - sources += [ - "debug/proc_maps_linux.cc", - "debug/proc_maps_linux.h", - "files/dir_reader_linux.h", - "files/scoped_file_linux.cc", - "process/internal_linux.cc", - "process/internal_linux.h", - "process/memory_linux.cc", - "process/process_handle_linux.cc", - "process/process_iterator_linux.cc", - "process/process_linux.cc", - "process/process_metrics_linux.cc", - "threading/platform_thread_linux.cc", - "threading/thread_type_delegate.cc", - "threading/thread_type_delegate.h", - ] - } - - if (use_cobalt_customizations) { - sources += [ - "process/internal_linux.cc", - "process/internal_linux.h", - ] - } - - if (!use_cobalt_customizations && (is_linux || is_chromeos || is_android)) { - sources += [ - "files/file_path_watcher_inotify.cc", - "files/file_path_watcher_inotify.h", - ] - } - - all_dependent_configs = [] - defines = [ "BASE_IMPLEMENTATION" ] - data = [] - data_deps = [] - libs = [] - frameworks = [] - - configs += [ - "//build/config:precompiled_headers", - "//build/config/compiler:prevent_unsafe_narrowing", - "//build/config/compiler:wexit_time_destructors", - "//build/config/compiler:wglobal_constructors", - ] - - deps = [ - ":message_pump_buildflags", - "//base/allocator:buildflags", - "//base/allocator/partition_allocator:raw_ptr", - "//base/third_party/double_conversion", - "//base/third_party/dynamic_annotations", - "//build:blink_buildflags", - "//build:branding_buildflags", - "//build/config/compiler:compiler_buildflags", - "//third_party/modp_b64", - ] - - if (is_starboard) { - deps -= [ "//base/allocator/partition_allocator:raw_ptr" ] - # TODO: b/330221826 Change to |public_configs| with matching dependencies. - all_dependent_configs += [ ":starboard_config" ] - } - - if (is_starboard && current_toolchain != host_toolchain) { - deps += [ "//starboard:starboard_group" ] - } - - # `raw_ptr` cannot be made a component due to CRT symbol issues. - # Its gateway to being a component is through `//base`, so we have - # to provide the appropriate `#define` here. - defines += [ "IS_RAW_PTR_IMPL" ] - - if (is_apple) { - deps += [ "//base/time/buildflags:buildflags" ] - } - - if (build_rust_json_reader) { - deps += [ "//third_party/rust/serde_json_lenient/v0_1/wrapper" ] - } - - # native_unwinder_android is intended for use solely via a dynamic feature - # module, to avoid increasing Chrome's executable size. - assert_no_deps = [ ":native_unwinder_android" ] - - public_deps = [ - ":anchor_functions_buildflags", - ":base_static", - ":build_date", - ":cfi_buildflags", - ":clang_profiling_buildflags", - ":debugging_buildflags", - ":feature_list_buildflags", - ":ios_cronet_buildflags", - ":logging_buildflags", - ":orderfile_buildflags", - ":power_monitor_buildflags", - ":profiler_buildflags", - ":rust_buildflags", - ":sanitizer_buildflags", - ":synchronization_buildflags", - ":tracing_buildflags", - "//base/allocator/partition_allocator:buildflags", - "//base/numerics:base_numerics", - "//build:chromecast_buildflags", - "//build:chromeos_buildflags", - "//third_party/abseil-cpp:absl", - ] - - if (is_starboard) { - public_deps -= [ "//base/allocator/partition_allocator:buildflags" ] - } - - if (build_rust_base_conversions) { - # Base provides conversions between CXX types and base types (e.g. - # StringPiece). - public_deps += [ "//build/rust:cxx_cppdeps" ] - } - - if (use_custom_libcxx && enable_safe_libcxx && !is_debug) { - public_deps += [ ":nodebug_assertion" ] - } - - # Needed for <atomic> if using newer C++ library than sysroot, except if - # building inside the cros_sdk environment - use host_toolchain as a - # more robust check for this. - if (!use_sysroot && (is_android || is_chromeos || (is_linux && !is_castos)) && - host_toolchain != "//build/toolchain/cros:host" && !sb_is_modular) { - libs += [ "atomic" ] - } - - if (use_allocator_shim) { - sources += [ - "allocator/partition_allocator/shim/allocator_shim.cc", - "allocator/partition_allocator/shim/allocator_shim.h", - "allocator/partition_allocator/shim/allocator_shim_internals.h", - ] - if (use_partition_alloc) { - sources += [ - "allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_partition_alloc.cc", - "allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_partition_alloc.h", - ] - } - if (is_android) { - sources += [ - "allocator/partition_allocator/shim/allocator_shim_override_cpp_symbols.h", - "allocator/partition_allocator/shim/allocator_shim_override_linker_wrapped_symbols.h", - ] - all_dependent_configs += [ "//base/allocator:wrap_malloc_symbols" ] - } - if (is_apple) { - sources += [ - "allocator/early_zone_registration_mac.h", - "allocator/partition_allocator/shim/allocator_shim_override_mac_default_zone.h", - "allocator/partition_allocator/shim/allocator_shim_override_mac_symbols.h", - ] - configs += [ "//base/allocator:mac_no_default_new_delete_symbols" ] - } - if (is_chromeos || is_linux) { - sources += [ - "allocator/partition_allocator/shim/allocator_shim_override_cpp_symbols.h", - "allocator/partition_allocator/shim/allocator_shim_override_glibc_weak_symbols.h", - "allocator/partition_allocator/shim/allocator_shim_override_libc_symbols.h", - ] - } - if (is_win) { - sources += [ - "allocator/partition_allocator/shim/allocator_shim_override_ucrt_symbols_win.h", - "allocator/partition_allocator/shim/winheap_stubs_win.cc", - "allocator/partition_allocator/shim/winheap_stubs_win.h", - ] - } - - if (!use_partition_alloc_as_malloc) { - if (is_android) { - sources += [ "allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_linker_wrapped_symbols.cc" ] - } - if (is_apple) { - sources += [ "allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_mac_zoned_malloc.cc" ] - } - if (is_chromeos || is_linux) { - sources += [ "allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_glibc.cc" ] - } - if (is_win) { - sources += [ "allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_winheap.cc" ] - } - } - } - - if (use_clang_profiling) { - # Call-sites use this conditional on the CLANG_PROFILING macro, for clarity. - sources += [ - "test/clang_profiling.cc", - "test/clang_profiling.h", - ] - } - - # Allow more direct string conversions on platforms with native utf8 - # strings - if (is_apple || is_chromeos || is_castos || is_cast_android || is_fuchsia) { - defines += [ "SYSTEM_NATIVE_UTF8" ] - } - - # Android. - if (is_android && !use_cobalt_customizations) { - sources += [ - "android/android_hardware_buffer_compat.cc", - "android/android_hardware_buffer_compat.h", - "android/android_image_reader_abi.h", - "android/android_image_reader_compat.cc", - "android/android_image_reader_compat.h", - "android/apk_assets.cc", - "android/apk_assets.h", - "android/application_status_listener.cc", - "android/application_status_listener.h", - "android/base_feature_list.cc", - "android/base_features.cc", - "android/base_features.h", - "android/build_info.cc", - "android/build_info.h", - "android/bundle_utils.cc", - "android/bundle_utils.h", - "android/child_process_binding_types.h", - "android/child_process_service.cc", - "android/content_uri_utils.cc", - "android/content_uri_utils.h", - "android/cpu_features.cc", - "android/early_trace_event_binding.cc", - "android/early_trace_event_binding.h", - "android/event_log.cc", - "android/event_log.h", - "android/feature_list_jni.cc", - "android/features_jni.cc", - "android/field_trial_list.cc", - "android/important_file_writer_android.cc", - "android/int_string_callback.cc", - "android/int_string_callback.h", - "android/java_handler_thread.cc", - "android/java_handler_thread.h", - "android/java_heap_dump_generator.cc", - "android/java_heap_dump_generator.h", - "android/java_runtime.cc", - "android/java_runtime.h", - "android/library_loader/anchor_functions.cc", - "android/library_loader/anchor_functions.h", - "android/library_loader/library_prefetcher.cc", - "android/library_loader/library_prefetcher.h", - "android/library_loader/library_prefetcher_hooks.cc", - "android/locale_utils.cc", - "android/locale_utils.h", - "android/meminfo_dump_provider.cc", - "android/meminfo_dump_provider.h", - "android/memory_pressure_listener_android.cc", - "android/memory_pressure_listener_android.h", - "android/path_service_android.cc", - "android/path_utils.cc", - "android/path_utils.h", - "android/radio_utils.cc", - "android/radio_utils.h", - "android/reached_addresses_bitset.cc", - "android/reached_addresses_bitset.h", - "android/reached_code_profiler.cc", - "android/reached_code_profiler.h", - "android/remove_stale_data.cc", - "android/remove_stale_data.h", - "android/scoped_hardware_buffer_fence_sync.cc", - "android/scoped_hardware_buffer_fence_sync.h", - "android/scoped_hardware_buffer_handle.cc", - "android/scoped_hardware_buffer_handle.h", - "android/statistics_recorder_android.cc", - "android/sys_utils.cc", - "android/sys_utils.h", - "android/task_scheduler/post_task_android.cc", - "android/task_scheduler/post_task_android.h", - "android/task_scheduler/task_runner_android.cc", - "android/task_scheduler/task_runner_android.h", - "android/task_scheduler/task_traits_android.h", - "android/thread_instruction_count.cc", - "android/thread_instruction_count.h", - "android/timezone_utils.cc", - "android/timezone_utils.h", - "android/unguessable_token_android.cc", - "android/unguessable_token_android.h", - "debug/stack_trace_android.cc", - "files/file_util_android.cc", - "files/scoped_file_android.cc", - "memory/platform_shared_memory_mapper_android.cc", - "memory/platform_shared_memory_region_android.cc", - "message_loop/message_pump_android.cc", - "message_loop/message_pump_android.h", - "os_compat_android.cc", - "os_compat_android.h", - "process/process_android.cc", - "profiler/native_unwinder_android_map_delegate.h", - "profiler/native_unwinder_android_memory_regions_map.h", - "profiler/stack_sampler_android.cc", - "system/sys_info_android.cc", - "threading/platform_thread_android.cc", - - # Android uses some Linux sources. - "debug/elf_reader.cc", - "debug/elf_reader.h", - "debug/proc_maps_linux.cc", - "debug/proc_maps_linux.h", - "power_monitor/power_monitor_device_source_android.cc", - "process/internal_linux.cc", - "process/internal_linux.h", - "process/memory_linux.cc", - "process/process_handle_linux.cc", - "process/process_iterator_linux.cc", - "process/process_metrics_linux.cc", - "system/sys_info_linux.cc", - ] - - deps += [ - "//third_party/android_ndk:cpu_features", - "//third_party/ashmem", - ] - - # Needs to be a public config so that dependent targets link against it as - # well when doing a component build. - public_configs = [ ":android_system_libs" ] - - if (can_unwind_with_cfi_table) { - sources += [ - "trace_event/cfi_backtrace_android.cc", - "trace_event/cfi_backtrace_android.h", - ] - } - if (current_cpu == "arm") { - sources += [ - "profiler/chrome_unwind_info_android.cc", - "profiler/chrome_unwind_info_android.h", - "profiler/chrome_unwinder_android.cc", - "profiler/chrome_unwinder_android.h", - ] - } - - if (current_cpu == "arm64") { - sources += [ - "profiler/frame_pointer_unwinder.cc", - "profiler/frame_pointer_unwinder.h", - ] - } - - if (current_cpu != "arm" && current_cpu != "arm64") { - # The reached code profiler is only supported on Android arm arch. - sources -= [ "android/reached_code_profiler.cc" ] - sources += [ "android/reached_code_profiler_stub.cc" ] - } - - # This is actually a linker script, but it can be added to the link in the - # same way as a library. - libs += [ "android/library_loader/anchor_functions.lds" ] - } # is_android - - if (build_allocation_stack_trace_recorder) { - sources += [ - "debug/allocation_trace.cc", - "debug/allocation_trace.h", - ] - } - - if (is_robolectric) { - # Make jni.h available. - configs += [ "//third_party/jdk" ] - } - if ((is_android || is_robolectric) && !use_cobalt_customizations) { - sources += [ - "android/base_jni_onload.cc", - "android/base_jni_onload.h", - "android/callback_android.cc", - "android/callback_android.h", - "android/command_line_android.cc", - "android/java_exception_reporter.cc", - "android/java_exception_reporter.h", - "android/jni_android.cc", - "android/jni_android.h", - "android/jni_array.cc", - "android/jni_array.h", - "android/jni_generator/jni_generator_helper.h", - "android/jni_int_wrapper.h", - "android/jni_registrar.cc", - "android/jni_registrar.h", - "android/jni_string.cc", - "android/jni_string.h", - "android/jni_utils.cc", - "android/jni_utils.h", - "android/jni_weak_ref.cc", - "android/jni_weak_ref.h", - "android/library_loader/library_loader_hooks.cc", - "android/library_loader/library_loader_hooks.h", - "android/native_uma_recorder.cc", - "android/scoped_java_ref.cc", - "android/scoped_java_ref.h", - "android/trace_event_binding.cc", - "android/trace_event_binding.h", - ] - deps += [ ":base_jni_headers" ] - } # is_android || is_robolectric - - # Chromeos. - if (is_chromeos) { - sources += [ - "logging_chromeos.cc", - "system/sys_info_chromeos.cc", - ] - - sources += [ "power_monitor/power_monitor_device_source_chromeos.cc" ] - - if (current_cpu == "x64") { - sources += [ - "profiler/frame_pointer_unwinder.cc", - "profiler/frame_pointer_unwinder.h", - ] - } - } - - # Fuchsia. - if (is_fuchsia) { - sources += [ - "base_paths_fuchsia.cc", - "debug/debugger_fuchsia.cc", - "debug/elf_reader.cc", - "debug/elf_reader.h", - "debug/stack_trace_fuchsia.cc", - "file_descriptor_posix.cc", - "file_descriptor_posix.h", - "files/dir_reader_posix.h", - "files/file_path_watcher_stub.cc", # See crbug.com/851641. - "files/file_posix.cc", - "files/file_util_fuchsia.cc", - "files/memory_mapped_file_posix.cc", - "fuchsia/default_job.cc", - "fuchsia/default_job.h", - "fuchsia/fidl_event_handler.h", - "fuchsia/file_utils.cc", - "fuchsia/file_utils.h", - "fuchsia/filtered_service_directory.cc", - "fuchsia/filtered_service_directory.h", - "fuchsia/fuchsia_component_connect.h", - "fuchsia/fuchsia_logging.cc", - "fuchsia/fuchsia_logging.h", - "fuchsia/intl_profile_watcher.cc", - "fuchsia/intl_profile_watcher.h", - "fuchsia/koid.cc", - "fuchsia/koid.h", - "fuchsia/mem_buffer_util.cc", - "fuchsia/mem_buffer_util.h", - "fuchsia/process_context.cc", - "fuchsia/process_context.h", - "fuchsia/process_lifecycle.cc", - "fuchsia/process_lifecycle.h", - "fuchsia/scheduler.h", - "fuchsia/scoped_fx_logger.cc", - "fuchsia/scoped_fx_logger.h", - "fuchsia/scoped_service_binding.h", - "fuchsia/scoped_service_publisher.h", - "fuchsia/startup_context.cc", - "fuchsia/startup_context.h", - "fuchsia/system_info.cc", - "fuchsia/system_info.h", - "memory/platform_shared_memory_mapper_fuchsia.cc", - "memory/platform_shared_memory_region_fuchsia.cc", - "message_loop/message_pump_fuchsia.cc", - "message_loop/message_pump_fuchsia.h", - "message_loop/watchable_io_message_pump_posix.cc", - "message_loop/watchable_io_message_pump_posix.h", - "native_library_fuchsia.cc", - "posix/eintr_wrapper.h", - "posix/file_descriptor_shuffle.cc", - "posix/file_descriptor_shuffle.h", - "posix/global_descriptors.cc", - "posix/global_descriptors.h", - "posix/safe_strerror.cc", - "posix/safe_strerror.h", - "process/kill_fuchsia.cc", - "process/launch_fuchsia.cc", - "process/memory_fuchsia.cc", - "process/process_fuchsia.cc", - "process/process_handle_fuchsia.cc", - "process/process_iterator_fuchsia.cc", - "process/process_metrics_fuchsia.cc", - "profiler/module_cache_posix.cc", - "rand_util_fuchsia.cc", - "strings/string_util_posix.h", - "strings/sys_string_conversions_posix.cc", - "synchronization/condition_variable_posix.cc", - "synchronization/lock_impl_posix.cc", - "synchronization/waitable_event_posix.cc", - "synchronization/waitable_event_watcher_posix.cc", - "system/sys_info_fuchsia.cc", - "threading/platform_thread_fuchsia.cc", - "threading/platform_thread_posix.cc", - "threading/thread_local_storage_posix.cc", - "time/time_conversion_posix.cc", - "time/time_exploded_icu.cc", - "time/time_fuchsia.cc", - "timer/hi_res_timer_manager_posix.cc", - ] - - # These only need to be public deps because of includes of their headers - # by public //base headers, which requires they be on the include path. - # TODO(https://crbug.com/841171): Move these back to |deps|. - public_deps += [ - "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.component.runner:fuchsia.component.runner_hlcpp", - "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.intl:fuchsia.intl_hlcpp", - "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.io:fuchsia.io_cpp", - "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.io:fuchsia.io_hlcpp", - "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.logger:fuchsia.logger_cpp", - "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.mem:fuchsia.mem_hlcpp", - "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.process.lifecycle:fuchsia.process.lifecycle_cpp", - "//third_party/fuchsia-sdk/sdk/pkg/async", - "//third_party/fuchsia-sdk/sdk/pkg/component_incoming_cpp", - "//third_party/fuchsia-sdk/sdk/pkg/fdio", - "//third_party/fuchsia-sdk/sdk/pkg/fidl_cpp", - "//third_party/fuchsia-sdk/sdk/pkg/fidl_cpp_wire", - "//third_party/fuchsia-sdk/sdk/pkg/sync", - "//third_party/fuchsia-sdk/sdk/pkg/sys_cpp", - "//third_party/fuchsia-sdk/sdk/pkg/syslog_structured_backend", - "//third_party/fuchsia-sdk/sdk/pkg/vfs_cpp", - "//third_party/fuchsia-sdk/sdk/pkg/zx", - ] - - deps += [ - "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.buildinfo:fuchsia.buildinfo_cpp", - "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.hwinfo:fuchsia.hwinfo_cpp", - "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.io:fuchsia.io_cpp_hlcpp_conversion", - "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.media:fuchsia.media_cpp", - "//third_party/fuchsia-sdk/sdk/pkg/async-default", - "//third_party/fuchsia-sdk/sdk/pkg/async-loop-cpp", - "//third_party/fuchsia-sdk/sdk/pkg/async-loop-default", - "//third_party/fuchsia-sdk/sdk/pkg/fidl", - "//third_party/fuchsia-sdk/sdk/pkg/sys_inspect_cpp", - "//third_party/icu", - ] - - # Needed for users of spinning_mutex.h, which for performance reasons, - # contains inlined calls to `libsync` inside the header file. - # It appends an entry to the "libs" section of the dependent target. - public_configs = [ ":fuchsia_sync_lib" ] - } - - # Several helpers in //base are backed by BoringSSL; however, NaCl builds - # cannot easily depend on BoringSSL due to the nacl_io library, so NaCl builds - # still retain a dedicated local implementation of the functionality. - # - # TODO(crbug.com/702997) Use only boringssl when NaCl is removed. - sources += [ - "hash/md5.h", - "hash/md5_constexpr.h", - "hash/md5_constexpr_internal.h", - "hash/sha1.h", - ] - if (is_nacl) { - sources += [ - "hash/md5_nacl.cc", - "hash/md5_nacl.h", - "hash/sha1_nacl.cc", - "hash/sha1_nacl.h", - ] - } else { - sources += [ - "hash/md5_boringssl.cc", - "hash/md5_boringssl.h", - "hash/sha1_boringssl.cc", - "hash/sha1_boringssl.h", - ] - public_deps += [ "//third_party/boringssl" ] - } - - # NaCl. - if (is_nacl) { - # Explicitly include the linux file. - sources += [ - "debug/stack_trace_nacl.cc", - "files/file_path_watcher_stub.cc", - "memory/page_size_nacl.cc", - "os_compat_nacl.cc", - "os_compat_nacl.h", - "process/process_stubs.cc", - "rand_util_nacl.cc", - "sync_socket_nacl.cc", - "system/sys_info_nacl.cc", - "threading/platform_thread_linux.cc", - ] - } else { - sources += [ - "base_paths.cc", - "base_paths.h", - "cpu.cc", - "cpu.h", - "debug/crash_logging.cc", - "debug/crash_logging.h", - "debug/stack_trace.cc", - "debug/stack_trace.h", - "files/file_enumerator.cc", - "files/file_enumerator.h", - "files/file_proxy.cc", - "files/file_proxy.h", - "files/file_util.cc", - "files/file_util.h", - "files/important_file_writer.cc", - "files/important_file_writer.h", - "files/important_file_writer_cleaner.cc", - "files/important_file_writer_cleaner.h", - "files/scoped_temp_dir.cc", - "files/scoped_temp_dir.h", - "json/json_file_value_serializer.cc", - "json/json_file_value_serializer.h", - "memory/discardable_memory.cc", - "memory/discardable_memory.h", - "memory/discardable_memory_allocator.cc", - "memory/discardable_memory_allocator.h", - "memory/discardable_memory_internal.h", - "metrics/persistent_histogram_storage.cc", - "metrics/persistent_histogram_storage.h", - "native_library.cc", - "native_library.h", - "path_service.cc", - "path_service.h", - "scoped_native_library.cc", - "scoped_native_library.h", - "system/sys_info.cc", - "system/sys_info.h", - "system/sys_info_internal.h", - "task/thread_pool/initialization_util.cc", - "task/thread_pool/initialization_util.h", - ] - - if (is_starboard) { - sources -= [ - # Not used by cobalt. - "native_library.cc", - "native_library.h", - "scoped_native_library.cc", - "scoped_native_library.h", - ] - } - - if (is_win && !is_starboard) { - sources += [ - "base_paths_win.cc", - "base_paths_win.h", - ] - } - - if (is_mac && !is_starboard) { - sources += [ - "base_paths_mac.h", - "base_paths_mac.mm", - ] - } - - if (is_android && !is_starboard) { - sources += [ - "base_paths_android.cc", - "base_paths_android.h", - ] - } - - if (!use_cobalt_customizations && is_posix) { - sources += [ - "base_paths_posix.h", - "memory/madv_free_discardable_memory_allocator_posix.cc", - "memory/madv_free_discardable_memory_allocator_posix.h", - "memory/madv_free_discardable_memory_posix.cc", - "memory/madv_free_discardable_memory_posix.h", - "posix/unix_domain_socket.cc", - "posix/unix_domain_socket.h", - "rand_util_posix.cc", - "system/sys_info_posix.cc", - ] - } - - if (!use_cobalt_customizations && (is_posix || is_fuchsia)) { - sources += [ - "files/file_descriptor_watcher_posix.cc", - "files/file_descriptor_watcher_posix.h", - "files/file_enumerator_posix.cc", - "files/file_util_posix.cc", - "memory/page_size_posix.cc", - ] - } - - if ((!use_cobalt_customizations && is_posix && !is_ios) || is_fuchsia) { - sources += [ - "process/process_metrics_posix.cc", - "sync_socket_posix.cc", - ] - } - - if (!use_cobalt_customizations && is_posix && !is_apple) { - sources += [ - "native_library_posix.cc", - "posix/can_lower_nice_to.cc", - "posix/can_lower_nice_to.h", - "process/launch_posix.cc", - "profiler/module_cache_posix.cc", - "profiler/stack_base_address_posix.cc", - "profiler/stack_base_address_posix.h", - "profiler/stack_copier_signal.cc", - "profiler/stack_copier_signal.h", - "profiler/thread_delegate_posix.cc", - "profiler/thread_delegate_posix.h", - ] - } - - if (!use_cobalt_customizations && is_posix && !is_android) { - sources += [ "debug/stack_trace_posix.cc" ] - } - - if (!use_cobalt_customizations && is_posix && !is_ios) { - sources += [ - "process/kill_posix.cc", - "process/process_posix.cc", - ] - } - - if (use_blink) { - sources += [ - "memory/discardable_shared_memory.cc", - "memory/discardable_shared_memory.h", - "process/kill.cc", - "process/kill.h", - "process/launch.cc", - "process/launch.h", - "process/memory.cc", - "process/memory.h", - "process/process_iterator.cc", - "process/process_iterator.h", - "process/process_metrics.cc", - "process/process_metrics.h", - ] - } - - if ((is_linux || is_chromeos) && !is_starboard) { - sources += [ - "base_paths_posix.cc", - "debug/elf_reader.cc", - "debug/elf_reader.h", - "stack_canary_linux.cc", - "stack_canary_linux.h", - ] - } - if (use_partition_alloc) { - # Add stuff that doesn't work in NaCl. - sources += [ - # PartitionAlloc uses SpinLock, which doesn't work in NaCl (see below). - "allocator/partition_alloc_features.cc", - "allocator/partition_alloc_features.h", - "allocator/partition_alloc_support.cc", - "allocator/partition_alloc_support.h", - ] - } - - # Need this to pass gn check, because gn check doesn't see - # BUILDFLAG(USE_PARTITION_ALLOC). A linker will remove all - # partition_alloc code if use_partition_alloc = false because no code uses - # partition_alloc. - public_deps += [ - "allocator/partition_allocator:partition_alloc", - "allocator/partition_allocator:raw_ptr", - ] - } - - # Windows. - if (is_win && !is_starboard) { - sources += [ - "debug/debugger_win.cc", - "debug/gdi_debug_util_win.cc", - "debug/gdi_debug_util_win.h", - "debug/handle_hooks_win.cc", - "debug/handle_hooks_win.h", - "debug/invalid_access_win.cc", - "debug/invalid_access_win.h", - "debug/stack_trace_win.cc", - "enterprise_util.cc", - "enterprise_util.h", - "enterprise_util_win.cc", - "file_version_info_win.cc", - "file_version_info_win.h", - "files/file_enumerator_win.cc", - "files/file_path_watcher_win.cc", - "files/file_util_win.cc", - "files/file_win.cc", - "files/memory_mapped_file_win.cc", - "logging_win.cc", - "logging_win.h", - "memory/page_size_win.cc", - "memory/platform_shared_memory_mapper_win.cc", - "memory/platform_shared_memory_region_win.cc", - "message_loop/message_pump_win.cc", - "message_loop/message_pump_win.h", - "native_library_win.cc", - "power_monitor/battery_level_provider_win.cc", - "power_monitor/power_monitor_device_source_win.cc", - "power_monitor/speed_limit_observer_win.cc", - "power_monitor/speed_limit_observer_win.h", - "process/kill_win.cc", - "process/launch_win.cc", - "process/memory_win.cc", - "process/process_handle_win.cc", - "process/process_info_win.cc", - "process/process_iterator_win.cc", - "process/process_metrics_win.cc", - "process/process_win.cc", - "profiler/module_cache_win.cc", - "profiler/native_unwinder_win.cc", - "profiler/native_unwinder_win.h", - "profiler/stack_sampler_win.cc", - "profiler/suspendable_thread_delegate_win.cc", - "profiler/suspendable_thread_delegate_win.h", - "profiler/win32_stack_frame_unwinder.cc", - "profiler/win32_stack_frame_unwinder.h", - "rand_util_win.cc", - "scoped_clear_last_error_win.cc", - "strings/strcat_win.cc", - "strings/strcat_win.h", - "strings/string_number_conversions_win.cc", - "strings/string_number_conversions_win.h", - "strings/string_split_win.cc", - "strings/string_split_win.h", - "strings/string_util_win.cc", - "strings/string_util_win.h", - "strings/sys_string_conversions_win.cc", - "sync_socket_win.cc", - "synchronization/condition_variable_win.cc", - "synchronization/lock_impl_win.cc", - "synchronization/waitable_event_watcher_win.cc", - "synchronization/waitable_event_win.cc", - "system/sys_info_win.cc", - "threading/platform_thread_win.cc", - "threading/platform_thread_win.h", - "threading/thread_local_storage_win.cc", - "time/time_win.cc", - "timer/hi_res_timer_manager_win.cc", - "win/access_control_list.cc", - "win/access_control_list.h", - "win/access_token.cc", - "win/access_token.h", - "win/async_operation.h", - "win/atl.h", - "win/atl_throw.cc", - "win/atl_throw.h", - "win/com_init_balancer.cc", - "win/com_init_balancer.h", - "win/com_init_check_hook.cc", - "win/com_init_check_hook.h", - "win/com_init_util.cc", - "win/com_init_util.h", - "win/core_winrt_util.cc", - "win/core_winrt_util.h", - "win/current_module.h", - "win/dark_mode_support.cc", - "win/dark_mode_support.h", - "win/default_apps_util.cc", - "win/default_apps_util.h", - "win/embedded_i18n/language_selector.cc", - "win/embedded_i18n/language_selector.h", - "win/enum_variant.cc", - "win/enum_variant.h", - "win/event_trace_consumer.h", - "win/event_trace_controller.cc", - "win/event_trace_controller.h", - "win/event_trace_provider.cc", - "win/event_trace_provider.h", - "win/hstring_reference.cc", - "win/hstring_reference.h", - "win/i18n.cc", - "win/i18n.h", - "win/iat_patch_function.cc", - "win/iat_patch_function.h", - "win/map.h", - "win/message_window.cc", - "win/message_window.h", - "win/nt_status.cc", - "win/nt_status.h", - "win/object_watcher.cc", - "win/object_watcher.h", - "win/patch_util.cc", - "win/patch_util.h", - "win/pe_image_reader.cc", - "win/pe_image_reader.h", - "win/post_async_results.h", - "win/process_startup_helper.cc", - "win/process_startup_helper.h", - "win/propvarutil.h", - "win/reference.h", - "win/registry.cc", - "win/registry.h", - "win/resource_util.cc", - "win/resource_util.h", - "win/scoped_bstr.cc", - "win/scoped_bstr.h", - "win/scoped_co_mem.h", - "win/scoped_com_initializer.cc", - "win/scoped_com_initializer.h", - "win/scoped_gdi_object.h", - "win/scoped_handle.cc", - "win/scoped_handle.h", - "win/scoped_handle_verifier.cc", - "win/scoped_handle_verifier.h", - "win/scoped_hdc.h", - "win/scoped_hglobal.h", - "win/scoped_hstring.cc", - "win/scoped_hstring.h", - "win/scoped_localalloc.h", - "win/scoped_process_information.cc", - "win/scoped_process_information.h", - "win/scoped_propvariant.h", - "win/scoped_safearray.h", - "win/scoped_select_object.h", - "win/scoped_variant.cc", - "win/scoped_variant.h", - "win/scoped_windows_thread_environment.h", - "win/scoped_winrt_initializer.cc", - "win/scoped_winrt_initializer.h", - "win/security_descriptor.cc", - "win/security_descriptor.h", - "win/security_util.cc", - "win/security_util.h", - "win/shlwapi.h", - "win/shortcut.cc", - "win/shortcut.h", - "win/sid.cc", - "win/sid.h", - "win/sphelper.h", - "win/startup_information.cc", - "win/startup_information.h", - "win/variant_conversions.h", - "win/variant_vector.cc", - "win/variant_vector.h", - "win/vector.cc", - "win/vector.h", - "win/win_handle_types.h", - "win/win_handle_types_list.inc", - "win/win_util.cc", - "win/win_util.h", - "win/wincrypt_shim.h", - "win/windows_defines.inc", - "win/windows_h_disallowed.h", - "win/windows_types.h", - "win/windows_undefines.inc", - "win/windows_version.cc", - "win/windows_version.h", - "win/windowsx_shim.h", - "win/winrt_foundation_helpers.h", - "win/winrt_storage_util.cc", - "win/winrt_storage_util.h", - "win/wmi.cc", - "win/wmi.h", - "win/wrapped_window_proc.cc", - "win/wrapped_window_proc.h", - ] - - deps += [ "//base/win:base_win_buildflags" ] - - data_deps += [ "//build/win:runtime_libs" ] - - if (com_init_check_hook_disabled) { - defines += [ "COM_INIT_CHECK_HOOK_DISABLED" ] - } - - libs += [ - "cfgmgr32.lib", - "ntdll.lib", - "onecore.lib", - "powrprof.lib", - "propsys.lib", - "setupapi.lib", - "shcore.lib", - "userenv.lib", - "wbemuuid.lib", - "winmm.lib", - ] - all_dependent_configs += [ - ":base_win_linker_flags", - "//tools/win/DebugVisualizers:chrome", - ] - inputs = [ - # chrome.natvis listed as an input here instead of in - # //tools/win/DebugVisualizers:chrome to prevent unnecessary size increase - # in generated build files. - "//tools/win/DebugVisualizers/chrome.natvis", - ] - } - - if (!is_win) { - sources += [ - "file_descriptor_store.cc", - "file_descriptor_store.h", - ] - } - - # Desktop Mac. - if (is_mac && !is_starboard) { - sources += [ - "allocator/partition_allocator/shim/allocator_interception_mac.h", - "allocator/partition_allocator/shim/allocator_interception_mac.mm", - "allocator/partition_allocator/shim/malloc_zone_functions_mac.cc", - "allocator/partition_allocator/shim/malloc_zone_functions_mac.h", - "enterprise_util.cc", - "enterprise_util.h", - "enterprise_util_mac.mm", - "files/file_path_watcher_fsevents.cc", - "files/file_path_watcher_fsevents.h", - "files/file_path_watcher_kqueue.cc", - "files/file_path_watcher_kqueue.h", - "files/file_path_watcher_mac.cc", - "mac/authorization_util.h", - "mac/authorization_util.mm", - "mac/close_nocancel.cc", - "mac/launch_application.h", - "mac/launch_application.mm", - "mac/launchd.cc", - "mac/launchd.h", - "mac/mac_util.h", - "mac/mac_util.mm", - "mac/mach_port_rendezvous.cc", - "mac/mach_port_rendezvous.h", - "mac/os_crash_dumps.cc", - "mac/os_crash_dumps.h", - "mac/scoped_aedesc.h", - "mac/scoped_authorizationref.h", - "mac/scoped_authorizationref.mm", - "mac/scoped_cffiledescriptorref.h", - "mac/scoped_cftyperef.h", - "mac/scoped_dispatch_object.h", - "mac/scoped_ionotificationportref.h", - "mac/scoped_ioobject.h", - "mac/scoped_ioplugininterface.h", - "mac/scoped_launch_data.h", - "mac/scoped_mach_msg_destroy.h", - "mac/scoped_sending_event.h", - "mac/scoped_sending_event.mm", - "mac/wrap_cg_display.h", - "message_loop/message_pump_kqueue.cc", - "message_loop/message_pump_kqueue.h", - "native_library_mac.mm", - "power_monitor/battery_level_provider_mac.mm", - "power_monitor/battery_state_sampler_mac.cc", - "power_monitor/iopm_power_source_sampling_event_source.cc", - "power_monitor/iopm_power_source_sampling_event_source.h", - "power_monitor/power_monitor_device_source_mac.mm", - "power_monitor/thermal_state_observer_mac.h", - "power_monitor/thermal_state_observer_mac.mm", - "process/kill_mac.cc", - "process/launch_mac.cc", - "process/memory_mac.mm", - "process/port_provider_mac.cc", - "process/port_provider_mac.h", - "process/process_handle_mac.cc", - "process/process_info_mac.cc", - "process/process_iterator_mac.cc", - "process/process_mac.cc", - "process/process_metrics_mac.cc", - "profiler/frame_pointer_unwinder.cc", - "profiler/frame_pointer_unwinder.h", - "profiler/stack_sampler_mac.cc", - "profiler/suspendable_thread_delegate_mac.cc", - "profiler/suspendable_thread_delegate_mac.h", - "synchronization/waitable_event_watcher_mac.cc", - "system/sys_info_mac.mm", - "time/time_exploded_posix.cc", - ] - - libs += [ - "bsm", - "pmenergy", - "pmsample", - ] - frameworks += [ - "ApplicationServices.framework", - "AppKit.framework", - "CoreFoundation.framework", - "IOKit.framework", - "OpenDirectory.framework", - ] - } - - # Mac or iOS. - if (is_apple && !is_starboard) { - sources += [ - "file_version_info_mac.h", - "file_version_info_mac.mm", - "files/file_util_mac.mm", - "mac/backup_util.h", - "mac/backup_util.mm", - "mac/bundle_locations.h", - "mac/bundle_locations.mm", - "mac/call_with_eh_frame.cc", - "mac/call_with_eh_frame.h", - "mac/call_with_eh_frame_asm.S", - "mac/dispatch_source_mach.cc", - "mac/dispatch_source_mach.h", - "mac/foundation_util.h", - "mac/foundation_util.mm", - "mac/mac_logging.h", - "mac/mac_logging.mm", - "mac/mach_logging.cc", - "mac/mach_logging.h", - "mac/scoped_block.h", - "mac/scoped_mach_port.cc", - "mac/scoped_mach_port.h", - "mac/scoped_mach_vm.cc", - "mac/scoped_mach_vm.h", - "mac/scoped_nsautorelease_pool.cc", - "mac/scoped_nsautorelease_pool.h", - "mac/scoped_nsobject.h", - "mac/scoped_objc_class_swizzler.h", - "mac/scoped_objc_class_swizzler.mm", - "mac/scoped_typeref.h", - "memory/platform_shared_memory_mapper_mac.cc", - "memory/platform_shared_memory_region_mac.cc", - "message_loop/message_pump_mac.h", - "message_loop/message_pump_mac.mm", - "profiler/module_cache_mac.cc", - "strings/sys_string_conversions_mac.mm", - "synchronization/waitable_event_mac.cc", - "system/sys_info_apple.mm", - "threading/platform_thread_mac.mm", - "time/time_mac.mm", - ] - frameworks += [ "Security.framework" ] - } - - # Linux. - if (!use_cobalt_customizations && (is_linux || is_chromeos)) { - # TODO(brettw) this will need to be parameterized at some point. - linux_configs = [] - if (use_glib) { - linux_configs += [ "//build/config/linux:glib" ] - } - - configs += linux_configs - all_dependent_configs += linux_configs - sources += [ "system/sys_info_linux.cc" ] - if (!is_cronet_build) { - # These dependencies are not required on Android, and in the case - # of xdg_mime must be excluded due to licensing restrictions. - sources += [ - "nix/mime_util_xdg.cc", - "nix/mime_util_xdg.h", - "nix/xdg_util.cc", - "nix/xdg_util.h", - ] - defines += [ "USE_SYMBOLIZE" ] - deps += [ - "//base/third_party/symbolize", - "//base/third_party/xdg_mime", - "//base/third_party/xdg_user_dirs", - ] - } - } else { - if (!is_android || is_starboard) { - sources -= [ - "linux_util.cc", - "linux_util.h", - ] - } - } - - # iOS - if (is_ios && !is_starboard) { - sources += [ - "base_paths_mac.h", - "base_paths_mac.mm", - "critical_closure_internal_ios.mm", - "ios/block_types.h", - "ios/device_util.h", - "ios/device_util.mm", - "ios/ios_util.h", - "ios/ios_util.mm", - "ios/ns_error_util.h", - "ios/ns_error_util.mm", - "ios/ns_range.h", - "ios/scoped_critical_action.h", - "ios/scoped_critical_action.mm", - "mac/bridging.h", - "native_library_ios.mm", - "power_monitor/power_monitor_device_source_ios.mm", - "process/process_metrics_ios.cc", - "profiler/stack_sampler_ios.cc", - "system/sys_info_ios.mm", - ] - - if (use_blink) { - sources += [ - "files/file_path_watcher_kqueue.cc", - "files/file_path_watcher_kqueue.h", - "files/file_path_watcher_mac.cc", - "ios/sim_header_shims.h", - "mac/mach_port_rendezvous.cc", - "mac/mach_port_rendezvous.h", - "process/kill_mac.cc", - "process/launch_mac.cc", - "process/memory_mac.mm", - "process/port_provider_mac.cc", - "process/port_provider_mac.h", - "process/process_handle_mac.cc", - "process/process_iterator_ios.mm", - "process/process_mac.cc", - "process/process_metrics_posix.cc", - "process/process_posix.cc", - "sync_socket_posix.cc", - "synchronization/waitable_event_watcher_mac.cc", - ] - libs += [ "bsm" ] - } else { - sources += [ - "process/launch_ios.cc", - "process/memory_stubs.cc", - "process/process_stubs.cc", - ] - } - - if (is_cronet_build) { - sources += [ - "message_loop/message_pump_io_ios.cc", - "message_loop/message_pump_io_ios.h", - ] - } else { - sources += [ - "message_loop/message_pump_kqueue.cc", - "message_loop/message_pump_kqueue.h", - ] - } - - if (ios_stack_profiler_enabled) { - sources += [ - "profiler/frame_pointer_unwinder.cc", - "profiler/frame_pointer_unwinder.h", - "profiler/suspendable_thread_delegate_mac.cc", - "profiler/suspendable_thread_delegate_mac.h", - ] - } - if (use_allocator_shim) { - sources += [ - "allocator/partition_allocator/shim/allocator_interception_mac.h", - "allocator/partition_allocator/shim/allocator_interception_mac.mm", - "allocator/partition_allocator/shim/malloc_zone_functions_mac.cc", - "allocator/partition_allocator/shim/malloc_zone_functions_mac.h", - ] - } else { - sources -= [ - "sampling_heap_profiler/poisson_allocation_sampler.cc", - "sampling_heap_profiler/poisson_allocation_sampler.h", - "sampling_heap_profiler/sampling_heap_profiler.cc", - "sampling_heap_profiler/sampling_heap_profiler.h", - ] - } - - if (current_cpu == "x64" || current_cpu == "arm64") { - sources += [ "time/time_exploded_posix.cc" ] - } else { - sources += [ "time/time_exploded_ios.cc" ] - } - - frameworks += [ "UIKit.framework" ] - } - - if (use_blink || true) { - sources += [ - "files/file_path_watcher.cc", - "files/file_path_watcher.h", - "sync_socket.cc", - "sync_socket.h", - ] - } - - if (dep_libevent) { - deps += [ "//third_party/libevent" ] - } - - if (use_libevent) { - sources += [ - "message_loop/message_pump_libevent.cc", - "message_loop/message_pump_libevent.h", - ] - } - - if (enable_message_pump_epoll) { - sources += [ - "message_loop/message_pump_epoll.cc", - "message_loop/message_pump_epoll.h", - ] - } - - # Android and MacOS have their own custom shared memory handle - # implementations. e.g. due to supporting both POSIX and native handles. - if (!use_cobalt_customizations && is_posix && !is_android && !is_apple) { - sources += [ - "memory/platform_shared_memory_mapper_posix.cc", - "memory/platform_shared_memory_region_posix.cc", - ] - } - - if (!use_cobalt_customizations && is_posix && !is_apple) { - sources += [ - "strings/sys_string_conversions_posix.cc", - "synchronization/waitable_event_posix.cc", - "synchronization/waitable_event_watcher_posix.cc", - "threading/platform_thread_internal_posix.cc", - "threading/platform_thread_internal_posix.h", - "time/time_exploded_posix.cc", - "time/time_now_posix.cc", - ] - - # TODO(b/167763382) Find an alternate solution for Chromecast devices, since - # adding the icui18n and icuuc deps significantly increases the binary size. - if (!is_castos && !is_cast_android) { - sources += [ "time/time_exploded_icu.cc" ] - - # The ICU dependency is only needed on systems with a 32-bit time_t. - # However, that cannot be determined from build variables, like - # |current_cpu|, since some 32-bit systems have a 64-bit time_t (and vice - # versa). Thus, the dependency is taken here for all POSIX platforms and - # the compiler+linker should be able to easily detect when the ICU - # routines will not be called and delete them in the final linking. - deps += [ - "//third_party/icu:icui18n", - "//third_party/icu:icuuc", - ] - } - } - - if (!is_starboard) { - if ((is_posix && !is_apple && !is_android) || is_fuchsia) { - sources += [ "profiler/stack_sampler_posix.cc" ] - } - } - - if ((is_posix && !is_apple && !is_android && !is_chromeos) || is_fuchsia || is_starboard) { - sources += [ "power_monitor/power_monitor_device_source_stub.cc" ] - } - - # On ARC++-enabled ChromeOS system, we need TimeTicks::FromUptimeMillis to - # interpret time values sent from Android container. - if ((is_android || is_chromeos_ash) && !is_starboard) { - sources += [ "time/time_android.cc" ] - } - - if (use_glib) { - sources += [ - "message_loop/message_pump_glib.cc", - "message_loop/message_pump_glib.h", - ] - } - - if (enable_base_tracing) { - sources += [ - "trace_event/auto_open_close_event.h", - "trace_event/builtin_categories.cc", - "trace_event/builtin_categories.h", - "trace_event/category_registry.cc", - "trace_event/category_registry.h", - "trace_event/heap_profiler.h", - "trace_event/interned_args_helper.cc", - "trace_event/interned_args_helper.h", - "trace_event/log_message.cc", - "trace_event/log_message.h", - "trace_event/malloc_dump_provider.cc", - "trace_event/malloc_dump_provider.h", - "trace_event/memory_allocator_dump.cc", - "trace_event/memory_allocator_dump.h", - "trace_event/memory_dump_manager.cc", - "trace_event/memory_dump_manager.h", - "trace_event/memory_dump_manager_test_utils.h", - "trace_event/memory_dump_provider.h", - "trace_event/memory_dump_provider_info.cc", - "trace_event/memory_dump_provider_info.h", - "trace_event/memory_dump_request_args.cc", - "trace_event/memory_dump_request_args.h", - "trace_event/memory_dump_scheduler.cc", - "trace_event/memory_dump_scheduler.h", - "trace_event/memory_infra_background_allowlist.cc", - "trace_event/memory_infra_background_allowlist.h", - "trace_event/memory_pressure_level_proto.cc", - "trace_event/memory_pressure_level_proto.h", - "trace_event/memory_usage_estimator.cc", - "trace_event/memory_usage_estimator.h", - "trace_event/optional_trace_event.h", - "trace_event/process_memory_dump.cc", - "trace_event/process_memory_dump.h", - "trace_event/trace_arguments.cc", - "trace_event/trace_arguments.h", - "trace_event/trace_buffer.cc", - "trace_event/trace_buffer.h", - "trace_event/trace_category.h", - "trace_event/trace_config.cc", - "trace_event/trace_config.h", - "trace_event/trace_config_category_filter.cc", - "trace_event/trace_config_category_filter.h", - "trace_event/trace_conversion_helper.h", - "trace_event/trace_event.h", - "trace_event/trace_event_impl.cc", - "trace_event/trace_event_impl.h", - "trace_event/trace_event_memory_overhead.cc", - "trace_event/trace_event_memory_overhead.h", - "trace_event/trace_log.cc", - "trace_event/trace_log.h", - "trace_event/trace_log_constants.cc", - "trace_event/traced_value.cc", - "trace_event/traced_value.h", - "trace_event/traced_value_support.h", - "trace_event/tracing_agent.cc", - "trace_event/tracing_agent.h", - "trace_event/typed_macros.h", - "trace_event/typed_macros_embedder_support.h", - "trace_event/typed_macros_internal.cc", - "trace_event/typed_macros_internal.h", - "tracing/perfetto_platform.cc", - "tracing/perfetto_platform.h", - "tracing/perfetto_task_runner.cc", - "tracing/perfetto_task_runner.h", - "tracing/trace_time.cc", - "tracing/trace_time.h", - "tracing/tracing_tls.cc", - "tracing/tracing_tls.h", - ] - if (use_cobalt_customizations && use_xcode_clang) { - defines = [ "USES_XCODE_CLANG" ] - } - - public_deps += [ - "//base/tracing/protos:chrome_track_event_zero", - "//third_party/perfetto:libperfetto", - ] - - deps += [ - "//base/tracing/protos:chrome_track_event", - "//third_party/perfetto/include/perfetto/protozero", - ] - - all_dependent_configs += [ - ":perfetto_config", - "//third_party/perfetto/gn:public_config", - ] - - if (is_win && !is_starboard) { - sources += [ - "trace_event/trace_event_etw_export_win.cc", - "trace_event/trace_event_etw_export_win.h", - "trace_event/trace_logging_minimal_win.cc", - "trace_event/trace_logging_minimal_win.h", - ] - } - - if (is_android && !is_starboard) { - sources += [ - "trace_event/application_state_proto_android.cc", - "trace_event/application_state_proto_android.h", - "trace_event/cpufreq_monitor_android.cc", - "trace_event/cpufreq_monitor_android.h", - "trace_event/java_heap_dump_provider_android.cc", - "trace_event/java_heap_dump_provider_android.h", - ] - } - - if (use_partition_alloc_as_malloc) { - sources += [ - "trace_event/address_space_dump_provider.cc", - "trace_event/address_space_dump_provider.h", - ] - } - } else { - sources += [ - "trace_event/trace_event_stub.cc", - "trace_event/trace_event_stub.h", - ] - } - - if (using_sanitizer && !is_fuchsia) { - data += [ "//tools/valgrind/asan/" ] - data_deps += - [ "//build/config/clang:llvm-symbolizer_data($host_toolchain)" ] - } - - if (!is_debug) { - configs -= [ "//build/config/compiler:default_optimization" ] - configs += [ "//build/config/compiler:optimize_max" ] - } - - if (!use_allocation_event_dispatcher && (!is_ios || use_allocator_shim)) { - # The old hooks depend on PoissonAllocationSampler, which is not always enabled. - sources += [ - "allocator/dispatcher/standard_hooks.cc", - "allocator/dispatcher/standard_hooks.h", - ] - } -} - -# Build flags for Control Flow Integrity -# https://www.chromium.org/developers/testing/control-flow-integrity -buildflag_header("cfi_buildflags") { - header = "cfi_buildflags.h" - - flags = [ - # TODO(pcc): remove CFI_CAST_CHECK, see https://crbug.com/626794. - "CFI_CAST_CHECK=$is_cfi && $use_cfi_cast", - "CFI_DIAG=$is_cfi && $use_cfi_diag", - "CFI_ICALL_CHECK=$is_cfi && $use_cfi_icall", - "CFI_ENFORCEMENT_TRAP=$is_cfi && !$use_cfi_diag", - "CFI_ENFORCEMENT_DIAGNOSTIC=$is_cfi && $use_cfi_diag && !$use_cfi_recover", - ] -} - -buildflag_header("debugging_buildflags") { - header = "debugging_buildflags.h" - header_dir = "base/debug" - enable_gdbinit_warning = - is_debug && (strip_absolute_paths_from_debug_symbols || use_custom_libcxx) - enable_lldbinit_warning = - is_debug && strip_absolute_paths_from_debug_symbols && is_mac - - # TODO(crbug.com/1420774): Try and enable these checks on Android too. - enable_commandline_sequence_checks = - (is_debug || dcheck_always_on) && !is_android - - flags = [ - "DCHECK_IS_CONFIGURABLE=$dcheck_is_configurable", - "ENABLE_PROFILING=$enable_profiling", - "CAN_UNWIND_WITH_FRAME_POINTERS=$can_unwind_with_frame_pointers", - "UNSAFE_DEVELOPER_BUILD=$is_unsafe_developer_build", - "CAN_UNWIND_WITH_CFI_TABLE=$can_unwind_with_cfi_table", - "EXCLUDE_UNWIND_TABLES=$exclude_unwind_tables", - "ENABLE_GDBINIT_WARNING=$enable_gdbinit_warning", - "ENABLE_LLDBINIT_WARNING=$enable_lldbinit_warning", - "EXPENSIVE_DCHECKS_ARE_ON=$enable_expensive_dchecks", - "ENABLE_STACK_TRACE_LINE_NUMBERS=$enable_stack_trace_line_numbers", - "ENABLE_COMMANDLINE_SEQUENCE_CHECKS=$enable_commandline_sequence_checks", - "ENABLE_ALLOCATION_STACK_TRACE_RECORDER=$build_allocation_stack_trace_recorder", - ] -} - -buildflag_header("feature_list_buildflags") { - header = "feature_list_buildflags.h" - - if (is_chromeos_ash) { - flags = [ - "ENABLE_BANNED_BASE_FEATURE_PREFIX=true", - "BANNED_BASE_FEATURE_PREFIX=\"CrOSLateBoot\"", - ] - } else { - flags = [ "ENABLE_BANNED_BASE_FEATURE_PREFIX=false" ] - } -} - -buildflag_header("logging_buildflags") { - header = "logging_buildflags.h" - - flags = [ - "ENABLE_LOG_ERROR_NOT_REACHED=$enable_log_error_not_reached", - "USE_RUNTIME_VLOG=$use_runtime_vlog", - ] -} - -buildflag_header("orderfile_buildflags") { - header = "orderfile_buildflags.h" - header_dir = "base/android/orderfile" - using_order_profiling = is_android && use_order_profiling - using_devtools_dumping = - is_android && devtools_instrumentation_dumping && enable_base_tracing - - flags = [ - "DEVTOOLS_INSTRUMENTATION_DUMPING=$using_devtools_dumping", - "ORDERFILE_INSTRUMENTATION=$using_order_profiling", - ] -} - -buildflag_header("synchronization_buildflags") { - header = "synchronization_buildflags.h" - header_dir = "base/synchronization" - - flags = - [ "ENABLE_MUTEX_PRIORITY_INHERITANCE=$enable_mutex_priority_inheritance" ] -} - -buildflag_header("anchor_functions_buildflags") { - header = "anchor_functions_buildflags.h" - header_dir = "base/android/library_loader" - _supports_code_ordering = - is_android && (current_cpu == "arm" || current_cpu == "arm64" || - current_cpu == "x86" || current_cpu == "x64") - - flags = [ - "USE_LLD=$use_lld", - "SUPPORTS_CODE_ORDERING=$_supports_code_ordering", - ] -} - -buildflag_header("clang_profiling_buildflags") { - header = "clang_profiling_buildflags.h" - header_dir = "base" - - flags = [ - "CLANG_PROFILING=$use_clang_profiling", - "CLANG_PROFILING_INSIDE_SANDBOX=$use_clang_profiling_inside_sandbox", - "USE_CLANG_COVERAGE=$use_clang_coverage", - ] -} - -buildflag_header("sanitizer_buildflags") { - header = "sanitizer_buildflags.h" - header_dir = "base" - - flags = [ - "IS_HWASAN=$is_hwasan", - "USING_SANITIZER=$using_sanitizer", - ] -} - -buildflag_header("tracing_buildflags") { - header = "tracing_buildflags.h" - - flags = [ - "ENABLE_BASE_TRACING=$enable_base_tracing", - "USE_PERFETTO_CLIENT_LIBRARY=$use_perfetto_client_library", - "OPTIONAL_TRACE_EVENTS_ENABLED=$optional_trace_events_enabled", - ] -} - -buildflag_header("profiler_buildflags") { - header = "profiler_buildflags.h" - header_dir = "base/profiler" - - flags = [ - "ENABLE_ARM_CFI_TABLE=$enable_arm_cfi_table", - "IOS_STACK_PROFILER_ENABLED=$ios_stack_profiler_enabled", - ] -} - -buildflag_header("power_monitor_buildflags") { - header = "power_monitor_buildflags.h" - header_dir = "base/power_monitor" - _has_battery_provider_impl = is_win || is_mac - - flags = [ "HAS_BATTERY_LEVEL_PROVIDER_IMPL=$_has_battery_provider_impl" ] -} - -# This is the subset of files from base that should not be used with a dynamic -# library. Note that this library cannot depend on base because base depends on -# base_static. -static_library("base_static") { - sources = [ - "base_export.h", - "base_switches.cc", - "base_switches.h", - "immediate_crash.h", - ] - - deps = [ "//build:chromeos_buildflags" ] - - if (is_starboard && current_toolchain != host_toolchain) { - deps += [ "//starboard:starboard_group" ] - } - - if (is_win) { - sources += [ - "win/static_constants.cc", - "win/static_constants.h", - ] - - public_deps = [ "//base/win:pe_image" ] - - # Disable sanitizer coverage in win/pe_image.cc. It is called by the sandbox - # before sanitizer coverage can initialize. http://crbug.com/484711 - configs -= [ "//build/config/sanitizers:default_sanitizer_flags" ] - configs += - [ "//build/config/sanitizers:default_sanitizer_flags_but_coverage" ] - } - - if (!is_debug) { - configs -= [ "//build/config/compiler:default_optimization" ] - configs += [ "//build/config/compiler:optimize_max" ] - } -} - -if (use_custom_libcxx && enable_safe_libcxx && !is_debug) { - # nodebug_assertion.cc has to be in its own source_set instead of being - # included as a source in //base as otherwise its symbols won't be linked in - # if they end up in an archive. - source_set("nodebug_assertion") { - defines = [ "BASE_IMPLEMENTATION" ] - sources = [ "nodebug_assertion.cc" ] - deps = [ ":base_static" ] - } -} - -component("i18n") { - output_name = "base_i18n" - sources = [ - "i18n/base_i18n_export.h", - "i18n/base_i18n_switches.cc", - "i18n/base_i18n_switches.h", - "i18n/break_iterator.cc", - "i18n/break_iterator.h", - "i18n/case_conversion.cc", - "i18n/case_conversion.h", - "i18n/char_iterator.cc", - "i18n/char_iterator.h", - "i18n/character_encoding.cc", - "i18n/character_encoding.h", - "i18n/encoding_detection.cc", - "i18n/encoding_detection.h", - "i18n/file_util_icu.cc", - "i18n/file_util_icu.h", - "i18n/i18n_constants.cc", - "i18n/i18n_constants.h", - "i18n/icu_string_conversions.cc", - "i18n/icu_string_conversions.h", - "i18n/icu_util.cc", - "i18n/icu_util.h", - "i18n/message_formatter.cc", - "i18n/message_formatter.h", - "i18n/number_formatting.cc", - "i18n/number_formatting.h", - "i18n/rtl.cc", - "i18n/rtl.h", - "i18n/streaming_utf8_validator.cc", - "i18n/streaming_utf8_validator.h", - "i18n/string_compare.cc", - "i18n/string_compare.h", - "i18n/time_formatting.cc", - "i18n/time_formatting.h", - "i18n/timezone.cc", - "i18n/timezone.h", - "i18n/unicodestring.h", - "i18n/utf8_validator_tables.cc", - "i18n/utf8_validator_tables.h", - ] - defines = [ "BASE_I18N_IMPLEMENTATION" ] - configs += [ "//build/config/compiler:wexit_time_destructors" ] - public_deps = [ - "//third_party/ced", - "//third_party/icu", - ] - deps = [ - ":base", - "//base/third_party/dynamic_annotations", - "//build:chromecast_buildflags", - "//build:chromeos_buildflags", - "//starboard/client_porting/icu_init", - ] - - if (is_starboard) { - public_deps -= [ "//third_party/ced" ] - } - - if (!is_debug) { - if (is_starboard) { - configs -= [ "//starboard/build/config:size" ] - configs += [ "//starboard/build/config:speed" ] - } else { - configs -= [ "//build/config/compiler:default_optimization" ] - configs += [ "//build/config/compiler:optimize_max" ] - } - } - - if (is_chromeos_lacros) { - sources += [ - "i18n/icu_mergeable_data_file.cc", - "i18n/icu_mergeable_data_file.h", - ] - } - - if (is_mac && !is_starboard) { - frameworks = [ "CoreFoundation.framework" ] - } -} - -if (!is_starboard) { -test("base_perftests") { - sources = [ - "hash/hash_perftest.cc", - "message_loop/message_pump_perftest.cc", - "observer_list_perftest.cc", - "rand_util_perftest.cc", - "strings/string_util_perftest.cc", - "substring_set_matcher/substring_set_matcher_perftest.cc", - "task/job_perftest.cc", - "task/sequence_manager/sequence_manager_perftest.cc", - "task/thread_pool/thread_pool_perftest.cc", - "threading/counter_perftest.cc", - "threading/thread_local_storage_perftest.cc", - - # "test/run_all_unittests.cc", - "json/json_perftest.cc", - "synchronization/lock_perftest.cc", - "synchronization/waitable_event_perftest.cc", - "threading/thread_perftest.cc", - ] - - deps = [ - ":base", - ":debugging_buildflags", - "//base/test:test_support", - "//base/test:test_support_perf", - "//testing/gtest", - "//testing/perf", - ] - - if (use_partition_alloc) { - sources += [ - "allocator/partition_allocator/partition_alloc_perftest.cc", - "allocator/partition_allocator/partition_lock_perftest.cc", - ] - deps += [ ":partition_alloc_test_support" ] - } - - data_deps = [ - # Needed for isolate script to execute. - "//testing:run_perf_test", - ] - - if (is_android) { - deps += [ "//testing/android/native_test:native_test_native_code" ] - shard_timeout = 600 - } - - if (is_fuchsia) { - # Run these performance tests against the actual system. - run_as_chromium_system_test = true - } - - if (!is_official_build) { - # The extra data tables required by stack traces are turned off for official - # build, only do stack trace perftest for unofficial build - sources += [ "debug/stack_trace_perftest.cc" ] - } - - if (build_allocation_stack_trace_recorder) { - sources += [ "debug/allocation_trace_perftest.cc" ] - } -} - -test("base_i18n_perftests") { - sources = [ "i18n/streaming_utf8_validator_perftest.cc" ] - deps = [ - ":base", - ":i18n", - "//base/test:test_support", - "//base/test:test_support_perf", - "//testing/gtest", - ] -} -} - -if (!is_ios && !use_cobalt_customizations) { - executable("build_utf8_validator_tables") { - sources = [ "i18n/build_utf8_validator_tables.cc" ] - deps = [ - ":base", - "//build/win:default_exe_manifest", - "//third_party/icu:icuuc", - ] - } - - executable("check_example") { - sources = [ "check_example.cc" ] - deps = [ - ":base", - "//build/win:default_exe_manifest", - ] - } - - executable("json_perftest_decodebench") { - sources = [ "json/json_perftest_decodebench.cc" ] - deps = [ ":base" ] - } -} - -if (is_win && !is_starboard) { - # Target to manually rebuild pe_image_test.dll which is checked into - # base/test/data/pe_image. - shared_library("pe_image_test") { - sources = [ "win/pe_image_test.cc" ] - ldflags = [ - "/DELAYLOAD:cfgmgr32.dll", - "/DELAYLOAD:shell32.dll", - "/SUBSYSTEM:WINDOWS", - ] - libs = [ - "cfgmgr32.lib", - "shell32.lib", - ] - } - - loadable_module("scoped_handle_test_dll") { - sources = [ "win/scoped_handle_test_dll.cc" ] - deps = [ - ":base", - "//base/win:base_win_buildflags", - ] - } -} - -if ((is_win && (current_cpu == "x64" || current_cpu == "arm64") && !is_starboard) || - (is_mac && !is_starboard) || - (is_android && (current_cpu == "arm" || current_cpu == "arm64")) || - (is_chromeos && current_cpu == "x64")) { - # Must be a loadable module so that it can be loaded/unloaded at runtime - # during testing. - loadable_module("base_profiler_test_support_library") { - testonly = true - sources = [ "profiler/test_support_library.cc" ] - } -} - -if (is_android && (current_cpu == "arm" || current_cpu == "arm64") && !use_cobalt_customizations) { - # Use separate library for - # |LibunwindstackUnwinderAndroidTest.ReparsesMapsOnNewDynamicLibraryLoad| - # testcase. We can't use the existing `base_profiler_test_support_library` - # library for this test since this gets loaded by other tests and unloading - # a library in Android doesn't guarantee it will actually be unloaded. - # And in the test we would like to observe the change in /proc/self/maps - # on loading a dynamic library. - loadable_module("base_profiler_reparsing_test_support_library") { - testonly = true - sources = [ "profiler/test_support_library.cc" ] - } -} - -if (is_android && !use_cobalt_customizations) { - source_set("native_unwinder_android") { - # This target is intended to be used only within the stack_unwinder dynamic - # feature module, to avoid binary size increase in Chrome due to the - # libunwindstack dependency. The additional :* visibility is needed to allow - # use by base test targets. - visibility = [ - ":*", - "//chrome/android/modules/stack_unwinder/internal:*", - ] - sources = [ - "profiler/libunwindstack_unwinder_android.cc", - "profiler/libunwindstack_unwinder_android.h", - "profiler/native_unwinder_android.cc", - "profiler/native_unwinder_android.h", - ] - - public_deps = [ - ":base", - "//third_party/libunwindstack", - ] - } -} - -source_set("base_stack_sampling_profiler_test_util") { - testonly = true - sources = [ - "profiler/stack_sampling_profiler_test_util.cc", - "profiler/stack_sampling_profiler_test_util.h", - ] - deps = [ - ":base", - "//base/test:test_support", - "//testing/gtest", - ] - if (is_android && !use_cobalt_customizations) { - sources += [ - "profiler/stack_sampling_profiler_java_test_util.cc", - "profiler/stack_sampling_profiler_java_test_util.h", - ] - deps += [ - ":base_profiler_test_support_jni_headers", - ":native_unwinder_android", - ] - } -} - -copy("base_unittests_bundle_data") { - testonly = true - if (is_starboard) { - install_content = true - } - sources = [ - "//tools/metrics/histograms/enums.xml", - "test/data/file_util/binary_file.bin", - "test/data/file_util/binary_file_diff.bin", - "test/data/file_util/binary_file_same.bin", - "test/data/file_util/blank_line.txt", - "test/data/file_util/blank_line_crlf.txt", - "test/data/file_util/crlf.txt", - "test/data/file_util/different.txt", - "test/data/file_util/different_first.txt", - "test/data/file_util/different_last.txt", - "test/data/file_util/empty1.txt", - "test/data/file_util/empty2.txt", - "test/data/file_util/first1.txt", - "test/data/file_util/first2.txt", - "test/data/file_util/original.txt", - "test/data/file_util/same.txt", - "test/data/file_util/same_length.txt", - "test/data/file_util/shortened.txt", - "test/data/json/bom_feff.json", - "test/data/pe_image_reader/signed.exe", - "test/data/serializer_nested_test.json", - "test/data/serializer_test.json", - "test/data/serializer_test_nowhitespace.json", - ] - if (is_starboard) { - outputs = [ "$sb_static_contents_output_data_dir/test/base/{{source_target_relative}}" ] - } else { - outputs = [ "{{bundle_resources_dir}}/" + - "{{source_root_relative_dir}}/{{source_file_part}}" ] - } -} - -if (is_apple && !is_starboard) { - source_set("base_unittests_arc") { - testonly = true - sources = [ - "ios/crb_protocol_observers_unittest.mm", - "mac/bind_objc_block_unittest_arc.mm", - "mac/scoped_nsobject_unittest_arc.mm", - ] - configs += [ "//build/config/compiler:enable_arc" ] - deps = [ - ":base", - "//base/ios", - "//base/test:test_support", - "//testing/gtest", - ] - } -} - -if (!is_nacl && (is_linux || is_chromeos) && !use_cobalt_customizations) { - # This test must compile with -fstack-protector-all - source_set("stack_canary_linux_unittests") { - testonly = true - - sources = [ "stack_canary_linux_unittest.cc" ] - cflags = [ "-fstack-protector-all" ] - - deps = [ - "//base", - "//testing/gtest", - ] - } -} - -if (is_fuchsia) { - fidl_library("testfidl") { - library_name = "base.testfidl" - - sources = [ "fuchsia/test.test-fidl" ] - } - - # TODO(crbug.com/1416553): remove this once all users are migrated to natural bindings. - source_set("test_interface_impl") { - testonly = true - sources = [ - "fuchsia/test_interface_impl.cc", - "fuchsia/test_interface_impl.h", - ] - deps = [ - ":base", - "//testing/gtest", - ] - public_deps = [ - ":testfidl_hlcpp", - "//third_party/fuchsia-sdk/sdk/pkg/fidl_cpp", - "//third_party/fuchsia-sdk/sdk/pkg/zx", - ] - } - - source_set("test_interface_natural_impl") { - testonly = true - public = [ "fuchsia/test_interface_natural_impl.h" ] - sources = [ "fuchsia/test_interface_natural_impl.cc" ] - deps = [ - ":base", - "//testing/gtest", - "//third_party/fuchsia-sdk/sdk/pkg/async", - ] - public_deps = [ - ":testfidl_cpp", - "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.io:fuchsia.io_cpp_common", - "//third_party/fuchsia-sdk/sdk/pkg/fidl_cpp_wire", - "//third_party/fuchsia-sdk/sdk/pkg/sys_cpp", - ] - } - - source_set("test_log_listener_safe") { - testonly = true - sources = [ - "fuchsia/test_log_listener_safe.cc", - "fuchsia/test_log_listener_safe.h", - ] - deps = [ - ":base", - "//base/test:test_support", - "//testing/gtest", - ] - public_deps = [ - "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.logger:fuchsia.logger_hlcpp", - ] - } -} - -source_set("arm_bti_testfunctions") { - testonly = true - - sources = [] - - if (target_cpu == "arm64" && (is_linux || is_android)) { - sources = [ - "allocator/partition_allocator/arm_bti_test_functions.S", - "allocator/partition_allocator/arm_bti_test_functions.h", - ] - } -} - -test("base_unittests") { - sources = [ - "allocator/dispatcher/dispatcher_unittest.cc", - "allocator/dispatcher/initializer_unittest.cc", - "allocator/dispatcher/internal/dispatcher_internal_unittest.cc", - "allocator/dispatcher/testing/dispatcher_test.cc", - "allocator/dispatcher/testing/dispatcher_test.h", - "allocator/dispatcher/testing/observer_mock.h", - "allocator/dispatcher/testing/tools.h", - "allocator/dispatcher/tls_unittest.cc", - "allocator/partition_allocator/pointers/raw_ptr_test_support.h", - "allocator/partition_allocator/pointers/raw_ptr_unittest.cc", - "allocator/partition_allocator/pointers/raw_ref_unittest.cc", - "at_exit_unittest.cc", - "atomicops_unittest.cc", - "auto_reset_unittest.cc", - "barrier_callback_unittest.cc", - "barrier_closure_unittest.cc", - "base64_unittest.cc", - "base64url_unittest.cc", - "big_endian_unittest.cc", - "bit_cast_unittest.cc", - "bits_unittest.cc", - "build_time_unittest.cc", - "callback_list_unittest.cc", - "cancelable_callback_unittest.cc", - "check_is_test_unittest.cc", - "check_unittest.cc", - "command_line_unittest.cc", - "component_export_unittest.cc", - "containers/adapters_unittest.cc", - "containers/buffer_iterator_unittest.cc", - "containers/checked_iterators_unittest.cc", - "containers/circular_deque_unittest.cc", - "containers/contains_unittest.cc", - "containers/contiguous_iterator_unittest.cc", - "containers/enum_set_unittest.cc", - "containers/erase_unittest.cc", - "containers/extend_unittest.cc", - "containers/fixed_flat_map_unittest.cc", - "containers/fixed_flat_set_unittest.cc", - "containers/flat_map_unittest.cc", - "containers/flat_set_unittest.cc", - "containers/flat_tree_unittest.cc", - "containers/id_map_unittest.cc", - "containers/intrusive_heap_unittest.cc", - "containers/linked_list_unittest.cc", - "containers/lru_cache_unittest.cc", - "containers/small_map_unittest.cc", - "containers/span_unittest.cc", - "containers/stack_container_unittest.cc", - "containers/unique_ptr_adapters_unittest.cc", - "containers/vector_buffer_unittest.cc", - "cpu_unittest.cc", - "cxx17_backports_unittest.cc", - "cxx20_is_constant_evaluated_unittest.cc", - "debug/alias_unittest.cc", - "debug/asan_service_unittest.cc", - "debug/crash_logging_unittest.cc", - "debug/debugger_unittest.cc", - "debug/dump_without_crashing_unittest.cc", - "debug/stack_trace_unittest.cc", - "debug/task_trace_unittest.cc", - "environment_unittest.cc", - "feature_list_unittest.cc", - "files/file_enumerator_unittest.cc", - "files/file_error_or_unittest.cc", - "files/file_path_unittest.cc", - "files/file_path_watcher_unittest.cc", - "files/file_proxy_unittest.cc", - "files/file_unittest.cc", - "files/file_util_unittest.cc", - "files/important_file_writer_cleaner_unittest.cc", - "files/important_file_writer_unittest.cc", - "files/memory_mapped_file_unittest.cc", - "files/safe_base_name_unittest.cc", - "files/scoped_temp_dir_unittest.cc", - "functional/bind_unittest.cc", - "functional/callback_helpers_unittest.cc", - "functional/callback_unittest.cc", - "functional/function_ref_unittest.cc", - "functional/identity_unittest.cc", - "functional/invoke_unittest.cc", - "functional/not_fn_unittest.cc", - "functional/overloaded_unittest.cc", - "gmock_unittest.cc", - "hash/hash_unittest.cc", - "hash/legacy_hash_unittest.cc", - "hash/md5_constexpr_unittest.cc", - "hash/md5_unittest.cc", - "hash/sha1_unittest.cc", - "i18n/break_iterator_unittest.cc", - "i18n/case_conversion_unittest.cc", - "i18n/char_iterator_unittest.cc", - "i18n/character_encoding_unittest.cc", - "i18n/file_util_icu_unittest.cc", - "i18n/icu_string_conversions_unittest.cc", - "i18n/icu_util_unittest.cc", - "i18n/message_formatter_unittest.cc", - "i18n/number_formatting_unittest.cc", - "i18n/rtl_unittest.cc", - "i18n/streaming_utf8_validator_unittest.cc", - "i18n/string_search_unittest.cc", - "i18n/time_formatting_unittest.cc", - "i18n/timezone_unittest.cc", - "i18n/transliterator_unittest.cc", - "immediate_crash_unittest.cc", - "json/json_parser_unittest.cc", - "json/json_reader_unittest.cc", - "json/json_value_converter_unittest.cc", - "json/json_value_serializer_unittest.cc", - "json/json_writer_unittest.cc", - "json/string_escape_unittest.cc", - "json/values_util_unittest.cc", - "lazy_instance_unittest.cc", - "logging_unittest.cc", - "memory/aligned_memory_unittest.cc", - "memory/discardable_memory_backing_field_trial_unittest.cc", - "memory/discardable_shared_memory_unittest.cc", - "memory/memory_pressure_listener_unittest.cc", - "memory/platform_shared_memory_region_unittest.cc", - "memory/ptr_util_unittest.cc", - "memory/raw_ptr_asan_unittest.cc", - "memory/ref_counted_memory_unittest.cc", - "memory/ref_counted_unittest.cc", - "memory/safe_ref_unittest.cc", - "memory/shared_memory_hooks_unittest.cc", - "memory/shared_memory_mapping_unittest.cc", - "memory/shared_memory_region_unittest.cc", - "memory/singleton_unittest.cc", - "memory/unsafe_shared_memory_pool_unittest.cc", - "memory/values_equivalent_unittest.cc", - "memory/weak_auto_reset_unittest.cc", - "memory/weak_ptr_unittest.cc", - "message_loop/message_pump_glib_unittest.cc", - "message_loop/message_pump_unittest.cc", - "message_loop/work_id_provider_unittest.cc", - "metrics/bucket_ranges_unittest.cc", - "metrics/crc32_unittest.cc", - "metrics/field_trial_params_unittest.cc", - "metrics/field_trial_unittest.cc", - "metrics/histogram_base_unittest.cc", - "metrics/histogram_delta_serialization_unittest.cc", - "metrics/histogram_functions_unittest.cc", - "metrics/histogram_macros_unittest.cc", - "metrics/histogram_samples_unittest.cc", - "metrics/histogram_snapshot_manager_unittest.cc", - "metrics/histogram_threadsafe_unittest.cc", - "metrics/histogram_unittest.cc", - "metrics/metrics_hashes_unittest.cc", - "metrics/persistent_histogram_allocator_unittest.cc", - "metrics/persistent_histogram_storage_unittest.cc", - "metrics/persistent_memory_allocator_unittest.cc", - "metrics/persistent_sample_map_unittest.cc", - "metrics/ranges_manager_unittest.cc", - "metrics/sample_map_unittest.cc", - "metrics/sample_vector_unittest.cc", - "metrics/single_sample_metrics_unittest.cc", - "metrics/sparse_histogram_unittest.cc", - "metrics/statistics_recorder_unittest.cc", - "native_library_unittest.cc", - "no_destructor_unittest.cc", - "observer_list_threadsafe_unittest.cc", - "observer_list_unittest.cc", - "one_shot_event_unittest.cc", - "parameter_pack_unittest.cc", - "path_service_unittest.cc", - "pickle_unittest.cc", - "power_monitor/battery_level_provider_unittest.cc", - "power_monitor/battery_state_sampler_unittest.cc", - "power_monitor/moving_average_unittest.cc", - "power_monitor/power_monitor_device_source_unittest.cc", - "power_monitor/power_monitor_unittest.cc", - "power_monitor/timer_sampling_event_source_unittest.cc", - "process/environment_internal_unittest.cc", - "process/memory_unittest.cc", - "process/process_metrics_unittest.cc", - "process/process_unittest.cc", - "process/process_util_unittest.cc", - "profiler/metadata_recorder_unittest.cc", - "profiler/module_cache_unittest.cc", - "profiler/sample_metadata_unittest.cc", - "profiler/stack_buffer_unittest.cc", - "profiler/stack_copier_suspend_unittest.cc", - "profiler/stack_copier_unittest.cc", - "profiler/stack_sampler_unittest.cc", - "profiler/stack_sampling_profiler_unittest.cc", - "rand_util_unittest.cc", - "ranges/algorithm_unittest.cc", - "ranges/functional_unittest.cc", - "ranges/ranges_unittest.cc", - "run_loop_unittest.cc", - "safe_numerics_unittest.cc", - "sampling_heap_profiler/lock_free_address_hash_set_unittest.cc", - "scoped_add_feature_flags_unittest.cc", - "scoped_clear_last_error_unittest.cc", - "scoped_generic_unittest.cc", - "scoped_multi_source_observation_unittest.cc", - "scoped_native_library_unittest.cc", - "scoped_observation_unittest.cc", - "security_unittest.cc", - "sequence_checker_unittest.cc", - "sequence_token_unittest.cc", - "state_transitions_unittest.cc", - "stl_util_unittest.cc", - "strings/abseil_string_number_conversions_unittest.cc", - "strings/escape_unittest.cc", - "strings/no_trigraphs_unittest.cc", - "strings/pattern_unittest.cc", - "strings/safe_sprintf_unittest.cc", - "strings/strcat_unittest.cc", - "strings/string_number_conversions_unittest.cc", - "strings/string_piece_unittest.cc", - "strings/string_split_unittest.cc", - "strings/string_tokenizer_unittest.cc", - "strings/string_util_unittest.cc", - "strings/stringize_macros_unittest.cc", - "strings/stringprintf_unittest.cc", - "strings/sys_string_conversions_unittest.cc", - "strings/to_string_test.cc", - "strings/utf_offset_string_conversions_unittest.cc", - "strings/utf_string_conversions_unittest.cc", - "substring_set_matcher/string_pattern_unittest.cc", - "substring_set_matcher/substring_set_matcher_unittest.cc", - "supports_user_data_unittest.cc", - "sync_socket_unittest.cc", - "synchronization/atomic_flag_unittest.cc", - "synchronization/condition_variable_unittest.cc", - "synchronization/lock_unittest.cc", - "synchronization/waitable_event_unittest.cc", - "synchronization/waitable_event_watcher_unittest.cc", - "sys_byteorder_unittest.cc", - "system/sys_info_unittest.cc", - "system/system_monitor_unittest.cc", - "task/bind_post_task_unittest.cc", - "task/cancelable_task_tracker_unittest.cc", - "task/common/checked_lock_unittest.cc", - "task/common/operations_controller_unittest.cc", - "task/common/task_annotator_unittest.cc", - "task/default_delayed_task_handle_delegate_unittest.cc", - "task/deferred_sequenced_task_runner_unittest.cc", - "task/delayed_task_handle_unittest.cc", - "task/lazy_thread_pool_task_runner_unittest.cc", - "task/post_job_unittest.cc", - "task/scoped_set_task_priority_for_current_thread_unittest.cc", - "task/sequence_manager/atomic_flag_set_unittest.cc", - "task/sequence_manager/hierarchical_timing_wheel_unittest.cc", - "task/sequence_manager/lazily_deallocated_deque_unittest.cc", - "task/sequence_manager/sequence_manager_impl_unittest.cc", - "task/sequence_manager/task_order_unittest.cc", - "task/sequence_manager/task_queue_selector_unittest.cc", - "task/sequence_manager/task_queue_unittest.cc", - "task/sequence_manager/test/mock_time_message_pump_unittest.cc", - "task/sequence_manager/thread_controller_power_monitor_unittest.cc", - "task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc", - "task/sequence_manager/timing_wheel_unittest.cc", - "task/sequence_manager/wake_up_queue_unittest.cc", - "task/sequence_manager/work_deduplicator_unittest.cc", - "task/sequence_manager/work_queue_sets_unittest.cc", - "task/sequence_manager/work_queue_unittest.cc", - "task/sequenced_task_runner_unittest.cc", - "task/single_thread_task_executor_unittest.cc", - "task/single_thread_task_runner_unittest.cc", - "task/task_runner_unittest.cc", - "task/task_traits_unittest.cc", - "task/thread_pool/can_run_policy_test.h", - "task/thread_pool/delayed_priority_queue_unittest.cc", - "task/thread_pool/delayed_task_manager_unittest.cc", - "task/thread_pool/environment_config_unittest.cc", - "task/thread_pool/job_task_source_unittest.cc", - "task/thread_pool/pooled_single_thread_task_runner_manager_unittest.cc", - "task/thread_pool/priority_queue_unittest.cc", - "task/thread_pool/sequence_unittest.cc", - "task/thread_pool/service_thread_unittest.cc", - "task/thread_pool/task_source_sort_key_unittest.cc", - "task/thread_pool/task_tracker_unittest.cc", - "task/thread_pool/test_task_factory.cc", - "task/thread_pool/test_task_factory.h", - "task/thread_pool/test_utils.cc", - "task/thread_pool/test_utils.h", - "task/thread_pool/thread_group_impl_unittest.cc", - "task/thread_pool/thread_group_unittest.cc", - "task/thread_pool/thread_pool_impl_unittest.cc", - "task/thread_pool/tracked_ref_unittest.cc", - "task/thread_pool/worker_thread_set_unittest.cc", - "task/thread_pool/worker_thread_unittest.cc", - "task/thread_pool_unittest.cc", - "template_util_unittest.cc", - "test/gmock_callback_support_unittest.cc", - "test/gmock_move_support_unittest.cc", - "test/gtest_links_unittest.cc", - "test/gtest_tags_unittest.cc", - "test/gtest_xml_unittest_result_printer_unittest.cc", - "test/launcher/test_launcher_unittest.cc", - "test/launcher/test_results_tracker_unittest.cc", - "test/memory/dangling_ptr_instrumentation.cc", - "test/memory/dangling_ptr_instrumentation.h", - "test/metrics/histogram_enum_reader_unittest.cc", - "test/metrics/histogram_tester_unittest.cc", - "test/metrics/user_action_tester_unittest.cc", - "test/mock_callback_unittest.cc", - "test/rectify_callback_unittest.cc", - "test/repeating_test_future_unittest.cc", - "test/scoped_feature_list_unittest.cc", - "test/scoped_logging_settings.h", - "test/scoped_mock_clock_override_unittest.cc", - "test/scoped_mock_time_message_loop_task_runner_unittest.cc", - "test/scoped_run_loop_timeout_unittest.cc", - "test/task_environment_unittest.cc", - "test/test_future_unittest.cc", - "test/test_mock_time_task_runner_unittest.cc", - "test/test_pending_task_unittest.cc", - "test/test_waitable_event_unittest.cc", - "third_party/dynamic_annotations/dynamic_annotations_compiletest.cc", - "thread_annotations_unittest.cc", - "threading/hang_watcher_unittest.cc", - "threading/platform_thread_unittest.cc", - "threading/post_task_and_reply_impl_unittest.cc", - "threading/scoped_blocking_call_unittest.cc", - "threading/scoped_thread_priority_unittest.cc", - "threading/sequence_bound_unittest.cc", - "threading/sequence_local_storage_map_unittest.cc", - "threading/sequence_local_storage_slot_unittest.cc", - "threading/simple_thread_unittest.cc", - "threading/thread_checker_unittest.cc", - "threading/thread_collision_warner_unittest.cc", - "threading/thread_id_name_manager_unittest.cc", - "threading/thread_local_storage_unittest.cc", - "threading/thread_local_unittest.cc", - "threading/thread_restrictions_unittest.cc", - "threading/thread_unittest.cc", - "threading/watchdog_unittest.cc", - "time/pr_time_unittest.cc", - "time/time_delta_from_string_unittest.cc", - "time/time_unittest.cc", - "timer/elapsed_timer_unittest.cc", - "timer/hi_res_timer_manager_unittest.cc", - "timer/lap_timer_unittest.cc", - "timer/mock_timer_unittest.cc", - "timer/timer_unittest.cc", - "timer/wall_clock_timer_unittest.cc", - "token_unittest.cc", - "tools_sanity_unittest.cc", - "traits_bag_unittest.cc", - "tuple_unittest.cc", - "types/cxx23_to_underlying_unittest.cc", - "types/expected_unittest.cc", - "types/id_type_unittest.cc", - "types/optional_ref_unittest.cc", - "types/optional_unittest.cc", - "types/optional_util_unittest.cc", - "types/pass_key_unittest.cc", - "types/strong_alias_unittest.cc", - "types/supports_ostream_operator_test.cc", - "types/token_type_unittest.cc", - "types/variant_util_unittest.cc", - "unguessable_token_unittest.cc", - "uuid_unittest.cc", - "value_iterators_unittest.cc", - "values_unittest.cc", - "version_unittest.cc", - "vlog_unittest.cc", - ] - - if (use_cobalt_customizations) { - sources -= [ - # Cobalt builds don't include build timestamps. - "build_time_unittest.cc", - - # Since Starboard lacks abstractions for multi-processing, and shared - # memory is solely used for inter-process communication, we disable - # shared memory and process code in Cobalt. - "memory/discardable_memory_backing_field_trial_unittest.cc", - "memory/discardable_shared_memory_unittest.cc", - "memory/memory_pressure_listener_unittest.cc", - "memory/platform_shared_memory_region_unittest.cc", - "memory/shared_memory_hooks_unittest.cc", - "memory/shared_memory_mapping_unittest.cc", - "memory/shared_memory_region_unittest.cc", - "memory/unsafe_shared_memory_pool_unittest.cc", - - # MessagePumpUIStarboard lives on top of an external message pump - # and behaves differently than normal message pumps. - "message_loop/message_pump_unittest.cc", - "process/process_metrics_unittest.cc", - "process/process_unittest.cc", - "process/process_util_unittest.cc", - - # moduleCache is not implemented in Starboard. - "profiler/module_cache_unittest.cc", - - # TODO: b/327008491 - Not used by Cobalt, but should be tested to get - # closer to Chrome. - "feature_list_unittest.cc", - "immediate_crash_unittest.cc", - "metrics/field_trial_params_unittest.cc", - "native_library_unittest.cc", - "test/launcher/test_launcher_unittest.cc", - "test/launcher/test_results_tracker_unittest.cc", - "test/metrics/histogram_enum_reader_unittest.cc", - "test/scoped_feature_list_unittest.cc", - "threading/hang_watcher_unittest.cc", - "test/gtest_links_unittest.cc", - "test/gtest_tags_unittest.cc", - "test/gtest_xml_unittest_result_printer_unittest.cc", - - # SyncSocket is not implemented in Starboard. - "sync_socket_unittest.cc", - - # WaitableEventWatcher is not implemented by Starboard. - "synchronization/waitable_event_watcher_unittest.cc", - - # Depends on USE_PARTITION_ALLOC. - "allocator/partition_allocator/pointers/raw_ptr_test_support.h", - "allocator/partition_allocator/pointers/raw_ptr_unittest.cc", - "allocator/partition_allocator/pointers/raw_ref_unittest.cc", - - # Previously disabled tests. - "environment_unittest.cc", - "files/memory_mapped_file_unittest.cc", - ] - - if (is_win && cobalt_pending_clean_up) { - sources -= [ - "power_monitor/battery_state_sampler_unittest.cc", - "profiler/stack_sampling_profiler_unittest.cc", - "ranges/algorithm_unittest.cc", - "safe_numerics_unittest.cc", - "strings/string_util_unittest.cc", - "task/sequence_manager/atomic_flag_set_unittest.cc", - "test/test_future_unittest.cc", - "tools_sanity_unittest.cc", - "types/expected_unittest.cc", - - # NiceMock needs to be fixed - "threading/scoped_blocking_call_unittest.cc", - ] - cflags = [ "/wd4172" ] - } - - if (is_clang) { - cflags_cc = [ "-Wno-trigraphs" ] - } - } - - if (!is_cronet_build) { - # location_unittest.cc fails gn check for tracing-shimmed Cronet builds. - # Cronet buildbot do not run base_unittests, so exclusing this test - # instead of adding a nogncheck is preferred (crbug.com/1400235). - sources += [ "location_unittest.cc" ] - } - - if (build_rust_base_conversions) { - sources += [ - "containers/span_rust_unittest.cc", - "strings/string_piece_rust_unittest.cc", - ] - } - - if (use_custom_libcxx && enable_safe_libcxx) { - sources += [ "libcpp_hardening_test.cc" ] - } - - defines = [] - - deps = [ - ":arm_bti_testfunctions", - ":base", - ":base_stack_sampling_profiler_test_util", - ":feature_list_buildflags", - ":i18n", - ":sanitizer_buildflags", - "//base/allocator:buildflags", - "//base/test:native_library_test_utils", - "//base/test:run_all_unittests", - "//base/test:test_support", - "//base/third_party/dynamic_annotations", - "//build:blink_buildflags", - "//build:chromecast_buildflags", - "//build:chromeos_buildflags", - "//testing/gmock", - "//testing/gtest", - "//third_party/icu", - "//third_party/modp_b64", - ] - - data_deps = [ - ] - - if (is_android && enable_chrome_android_internal) { - data_deps += [ "//clank/build/bot/filters:base_unittests_filters" ] - } - - if (build_rust_base_conversions) { - deps += [ "//build/rust:cxx_cppdeps" ] - } - - if (is_apple && !is_starboard) { - public_deps = [ ":base_unittests_bundle_data" ] - - deps += [ ":base_unittests_arc" ] - } - - if (is_starboard){ - data_deps += [ - ":base_unittests_bundle_data", - "//cobalt/network:copy_ssl_certificates", - ] - } - - if (!is_ios && !is_starboard) { - sources += [ "allocator/partition_allocator/tagging_unittest.cc" ] - } - - data = [ - "test/data/", - "//tools/metrics/histograms/enums.xml", - ] - - if (is_win && !is_starboard) { - deps += [ "//base/win:base_win_buildflags" ] - - sources += [ - "debug/gdi_debug_util_win_unittest.cc", - "file_version_info_win_unittest.cc", - "process/launch_unittest_win.cc", - "test/fake_iasync_operation_win_unittest.cc", - "test/test_file_util_win_unittest.cc", - "test/test_reg_util_win_unittest.cc", - "threading/platform_thread_win_unittest.cc", - "time/time_win_unittest.cc", - "win/access_control_list_unittest.cc", - "win/access_token_unittest.cc", - "win/async_operation_unittest.cc", - "win/com_init_balancer_unittest.cc", - "win/com_init_check_hook_unittest.cc", - "win/com_init_util_unittest.cc", - "win/dark_mode_support_unittest.cc", - "win/dispatch_stub.cc", - "win/dispatch_stub.h", - "win/dllmain.cc", - "win/embedded_i18n/language_selector_unittest.cc", - "win/enum_variant_unittest.cc", - "win/event_trace_consumer_unittest.cc", - "win/event_trace_controller_unittest.cc", - "win/event_trace_provider_unittest.cc", - "win/hstring_reference_unittest.cc", - "win/i18n_unittest.cc", - "win/map_unittest.cc", - "win/message_window_unittest.cc", - "win/object_watcher_unittest.cc", - "win/pe_image_reader_unittest.cc", - "win/pe_image_unittest.cc", - "win/post_async_results_unittest.cc", - "win/reference_unittest.cc", - "win/registry_unittest.cc", - "win/scoped_bstr_unittest.cc", - "win/scoped_handle_unittest.cc", - "win/scoped_hstring_unittest.cc", - "win/scoped_localalloc_unittest.cc", - "win/scoped_process_information_unittest.cc", - "win/scoped_safearray_unittest.cc", - "win/scoped_variant_unittest.cc", - "win/scoped_winrt_initializer_unittest.cc", - "win/security_descriptor_unittest.cc", - "win/security_util_unittest.cc", - "win/shortcut_unittest.cc", - "win/sid_unittest.cc", - "win/startup_information_unittest.cc", - "win/variant_conversions_unittest.cc", - "win/variant_vector_unittest.cc", - "win/vector_unittest.cc", - "win/win_includes_unittest.cc", - "win/win_util_unittest.cc", - "win/windows_version_unittest.cc", - "win/winrt_storage_util_unittest.cc", - "win/wmi_unittest.cc", - "win/wrapped_window_proc_unittest.cc", - ] - - if (enable_cet_shadow_stack) { - sources += [ "win/cet_shadow_stack_unittest.cc" ] - } - - libs = [ - "ntdll.lib", - "onecore.lib", - ] - } - - if (!use_cobalt_customizations && (is_linux || is_chromeos)) { - sources += [ - "debug/proc_maps_linux_unittest.cc", - "files/scoped_file_linux_unittest.cc", - ] - - if (!!use_cobalt_customizations && !is_nacl) { - deps += [ ":stack_canary_linux_unittests" ] - } - } - - if (is_mac && !is_starboard) { - sources += [ - "allocator/partition_allocator/shim/allocator_interception_mac_unittest.mm", - "allocator/partition_allocator/shim/malloc_zone_functions_mac_unittest.cc", - "enterprise_util_mac_unittest.mm", - "mac/backup_util_unittest.mm", - "mac/bind_objc_block_unittest.mm", - "mac/call_with_eh_frame_unittest.mm", - "mac/dispatch_source_mach_unittest.cc", - "mac/foundation_util_unittest.mm", - "mac/mac_util_unittest.mm", - "mac/mach_port_rendezvous_unittest.cc", - "mac/scoped_mach_vm_unittest.cc", - "mac/scoped_nsobject_unittest.mm", - "mac/scoped_objc_class_swizzler_unittest.mm", - "mac/scoped_sending_event_unittest.mm", - "message_loop/message_pump_mac_unittest.mm", - "power_monitor/thermal_state_observer_mac_unittest.mm", - "process/memory_unittest_mac.h", - "process/memory_unittest_mac.mm", - "strings/sys_string_conversions_mac_unittest.mm", - ] - } - - if (is_apple && enable_mach_absolute_time_ticks && !is_starboard) { - sources += [ "time/time_mac_unittest.mm" ] - } - - if (!use_cobalt_customizations && is_posix) { - sources += [ - "files/dir_reader_posix_unittest.cc", - "files/file_descriptor_watcher_posix_unittest.cc", - "memory/madv_free_discardable_memory_allocator_posix_unittest.cc", - "memory/madv_free_discardable_memory_posix_unittest.cc", - "message_loop/fd_watch_controller_posix_unittest.cc", - "posix/file_descriptor_shuffle_unittest.cc", - "posix/unix_domain_socket_unittest.cc", - ] - if (!is_nacl && !is_apple) { - sources += [ - "profiler/stack_base_address_posix_unittest.cc", - "profiler/stack_copier_signal_unittest.cc", - "profiler/thread_delegate_posix_unittest.cc", - ] - } - } - - # Allow more direct string conversions on platforms with native utf8 - # strings - if (is_apple || is_chromeos || is_castos || is_cast_android || is_fuchsia) { - defines += [ "SYSTEM_NATIVE_UTF8" ] - } - - if (is_android && !use_cobalt_customizations) { - # Add unwind tables in base_unittests_apk test apk. The unwind tables are - # generated from debug info in the binary. Removing "default_symbols" and - # adding symbols config removes the "strip_debug" config that strips the - # debug info, on base unittests apk. - if (can_unwind_with_cfi_table || enable_arm_cfi_table) { - configs -= [ "//build/config/compiler:default_symbols" ] - if (symbol_level == 2) { - configs += [ "//build/config/compiler:symbols" ] - } else { - configs += [ "//build/config/compiler:minimal_symbols" ] - } - add_unwind_tables_in_apk = true - } - if (can_unwind_with_cfi_table) { - sources += [ "trace_event/cfi_backtrace_android_unittest.cc" ] - } - if (current_cpu == "arm") { - sources += [ - "profiler/chrome_unwind_info_android_unittest.cc", - "profiler/chrome_unwinder_android_unittest.cc", - ] - } - if (current_cpu == "arm64") { - sources += [ "profiler/frame_pointer_unwinder_unittest.cc" ] - } - if (!exclude_unwind_tables && - (current_cpu == "arm" || current_cpu == "arm64")) { - sources += [ - "profiler/libunwindstack_unwinder_android_unittest.cc", - "profiler/native_unwinder_android_unittest.cc", - ] - deps += [ - ":base_profiler_reparsing_test_support_library", - ":base_profiler_test_support_java", - ":base_profiler_test_support_jni_headers", - ":base_profiler_test_support_library", - ":native_unwinder_android", - ] - } - - sources += [ - "android/android_image_reader_compat_unittest.cc", - "android/application_status_listener_unittest.cc", - "android/child_process_unittest.cc", - "android/content_uri_utils_unittest.cc", - "android/java_handler_thread_unittest.cc", - "android/jni_android_unittest.cc", - "android/jni_array_unittest.cc", - "android/jni_string_unittest.cc", - "android/library_loader/library_prefetcher_unittest.cc", - "android/linker/linker_jni.cc", - "android/linker/linker_jni.h", - "android/linker/linker_unittest.cc", - "android/meminfo_dump_provider_unittest.cc", - "android/path_utils_unittest.cc", - "android/radio_utils_unittest.cc", - "android/reached_addresses_bitset_unittest.cc", - "android/scoped_java_ref_unittest.cc", - "android/sys_utils_unittest.cc", - "android/unguessable_token_android_unittest.cc", - "os_compat_android_unittest.cc", - ] - - # Android does not use test_launcher to run gtests. - sources -= [ - "process/process_unittest.cc", - "process/process_util_unittest.cc", - "test/gtest_links_unittest.cc", - "test/gtest_tags_unittest.cc", - "test/gtest_xml_unittest_result_printer_unittest.cc", - "test/launcher/test_launcher_unittest.cc", - "test/launcher/test_results_tracker_unittest.cc", - ] - deps += [ - ":base_java_unittest_support", - "//base/test:test_support_java", - ] - } - - if (build_allocation_stack_trace_recorder) { - sources += [ "debug/allocation_trace_unittest.cc" ] - } - - if (is_starboard) { - sources += [ "message_loop/message_pump_io_starboard_unittest.cc" ] - } - - if (is_ios && !is_starboard) { - sources += [ - "ios/device_util_unittest.mm", - "ios/scoped_critical_action_unittest.mm", - ] - - if (is_cronet_build) { - sources += [ "message_loop/message_pump_io_ios_unittest.cc" ] - } else { - sources += [ "message_loop/message_pump_kqueue_unittest.cc" ] - } - - # ios does not use test_launcher to run gtests. - sources -= [ - "files/file_path_watcher_unittest.cc", - "memory/discardable_shared_memory_unittest.cc", - "process/memory_unittest.cc", - "process/process_unittest.cc", - "process/process_util_unittest.cc", - "sync_socket_unittest.cc", - "synchronization/waitable_event_watcher_unittest.cc", - "test/gtest_links_unittest.cc", - "test/gtest_tags_unittest.cc", - "test/gtest_xml_unittest_result_printer_unittest.cc", - "test/launcher/test_launcher_unittest.cc", - "test/launcher/test_results_tracker_unittest.cc", - ] - - sources += [ - # Pull in specific Mac files for iOS. - "mac/backup_util_unittest.mm", - "mac/bind_objc_block_unittest.mm", - "mac/foundation_util_unittest.mm", - "mac/scoped_nsobject_unittest.mm", - "strings/sys_string_conversions_mac_unittest.mm", - ] - - sources -= [ - # The immediate crash tests require shared libraries, which we don't - # support loading on iOS. - "immediate_crash_unittest.cc", - ] - - if (use_allocator_shim) { - sources += [ - "allocator/partition_allocator/shim/allocator_interception_mac_unittest.mm", - "allocator/partition_allocator/shim/malloc_zone_functions_mac_unittest.cc", - ] - } - - if (use_blink) { - sources += [ "files/file_path_watcher_unittest.cc" ] - } - } - - if (use_partition_alloc) { - sources += [ - "allocator/partition_allocator/address_pool_manager_unittest.cc", - "allocator/partition_allocator/address_space_randomization_unittest.cc", - "allocator/partition_allocator/compressed_pointer_unittest.cc", - "allocator/partition_allocator/freeslot_bitmap_unittest.cc", - "allocator/partition_allocator/hardening_unittest.cc", - "allocator/partition_allocator/memory_reclaimer_unittest.cc", - "allocator/partition_allocator/page_allocator_unittest.cc", - "allocator/partition_allocator/partition_alloc_base/bits_pa_unittest.cc", - "allocator/partition_allocator/partition_alloc_base/component_export_pa_unittest.cc", - "allocator/partition_allocator/partition_alloc_base/cpu_pa_unittest.cc", - "allocator/partition_allocator/partition_alloc_base/cxx17_backports_pa_unittest.cc", - "allocator/partition_allocator/partition_alloc_base/logging_pa_unittest.cc", - "allocator/partition_allocator/partition_alloc_base/rand_util_pa_unittest.cc", - "allocator/partition_allocator/partition_alloc_base/scoped_clear_last_error_pa_unittest.cc", - "allocator/partition_allocator/partition_alloc_base/strings/stringprintf_pa_unittest.cc", - "allocator/partition_allocator/partition_alloc_base/thread_annotations_pa_unittest.cc", - "allocator/partition_allocator/partition_alloc_unittest.cc", - "allocator/partition_allocator/partition_lock_unittest.cc", - "allocator/partition_allocator/reverse_bytes_unittest.cc", - "allocator/partition_allocator/thread_cache_unittest.cc", - ] - - if (use_starscan) { - sources += [ - "allocator/partition_allocator/starscan/pcscan_scheduling_unittest.cc", - "allocator/partition_allocator/starscan/pcscan_unittest.cc", - "allocator/partition_allocator/starscan/scan_loop_unittest.cc", - "allocator/partition_allocator/starscan/stack/stack_unittest.cc", - "allocator/partition_allocator/starscan/state_bitmap_unittest.cc", - ] - } - - if ((is_android || is_linux) && target_cpu == "arm64") { - cflags = [ - "-Xclang", - "-target-feature", - "-Xclang", - "+mte", - ] - } - - if (is_android) { - sources += [ - "allocator/partition_allocator/partition_alloc_base/files/file_path_pa_unittest.cc", - "allocator/partition_allocator/partition_alloc_base/native_library_pa_unittest.cc", - ] - } else if (is_fuchsia) { - sources += [ "allocator/partition_allocator/partition_alloc_base/fuchsia/fuchsia_logging_pa_unittest.cc" ] - } - - if (enable_pkeys && is_debug && !is_component_build) { - # This test requires RELRO, which is not enabled in component builds. - # Also, require a debug build, since we only disable stack protectors in - # debug builds in partition alloc (see below why it's needed). - sources += [ "allocator/partition_allocator/pkey_unittest.cc" ] - - # We want to test the pkey code without access to memory that is not - # pkey-tagged. This will allow us to catch unintended memory accesses - # that could break our security assumptions. The stack protector reads a - # value from the TLS which won't be pkey-tagged, hence disabling it for - # the test. - configs += [ ":no_stack_protector" ] - } - - deps += [ ":partition_alloc_test_support" ] - } - - if (is_mac && !is_starboard) { - sources += [ "message_loop/message_pump_kqueue_unittest.cc" ] - frameworks = [ - "CoreFoundation.framework", - "Foundation.framework", - "OpenCL.framework", - ] - if (current_cpu == "x64") { - deps += [ ":base_profiler_test_support_library" ] - } - } - - if (!use_cobalt_customizations && (is_fuchsia || is_linux || is_chromeos)) { - sources += [ - "debug/elf_reader_unittest.cc", - "debug/test_elf_image_builder.cc", - "debug/test_elf_image_builder.h", - ] - - deps += [ "//base/test:malloc_wrapper" ] - defines += [ - # This library is used by ElfReaderTest to test reading elf files. - "MALLOC_WRAPPER_LIB=\"${shlib_prefix}malloc_wrapper${shlib_extension}\"", - ] - - if (!is_component_build) { - # Set rpath to find libmalloc_wrapper.so even in a non-component build. - configs += [ "//build/config/gcc:rpath_for_built_shared_libraries" ] - } - } - - if (is_chromeos_lacros) { - sources += [ "i18n/icu_mergeable_data_file_unittest.cc" ] - } - - if (!use_cobalt_customizations && (is_linux || is_chromeos_lacros)) { - sources += [ - "linux_util_unittest.cc", - "nix/xdg_util_unittest.cc", - ] - } - - if (!use_glib) { - sources -= [ "message_loop/message_pump_glib_unittest.cc" ] - } - - if (use_libevent) { - sources += [ "message_loop/message_pump_libevent_unittest.cc" ] - deps += [ - ":message_pump_buildflags", - "//third_party/libevent", - ] - } - - if (is_fuchsia) { - sources += [ - "files/dir_reader_posix_unittest.cc", - "files/file_descriptor_watcher_posix_unittest.cc", - "fuchsia/fidl_event_handler_unittest.cc", - "fuchsia/file_utils_unittest.cc", - "fuchsia/filtered_service_directory_unittest.cc", - "fuchsia/fuchsia_logging_unittest.cc", - "fuchsia/intl_profile_watcher_unittest.cc", - "fuchsia/mem_buffer_util_unittest.cc", - "fuchsia/scoped_natural_service_binding_unittest.cc", - "fuchsia/scoped_natural_service_publisher_unittest.cc", - "fuchsia/scoped_service_binding_unittest.cc", - "fuchsia/scoped_service_publisher_unittest.cc", - "fuchsia/system_build_info_unittest.cc", - "fuchsia/system_product_info_unittest.cc", - "fuchsia/test_component_context_for_process_unittest.cc", - "fuchsia/time_zone_data_unittest.cc", - "message_loop/fd_watch_controller_posix_unittest.cc", - "posix/file_descriptor_shuffle_unittest.cc", - ] - - # FilePatchWatcherImpl is not implemented. See crbug.com/851641. - # TODO(crbug.com/1430566): Convert this subtraction to a condition where - # it is added. - sources -= [ "files/file_path_watcher_unittest.cc" ] - - deps += [ - ":test_interface_impl", - ":test_interface_natural_impl", - ":test_log_listener_safe", - "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.buildinfo:fuchsia.buildinfo_cpp", - "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.buildinfo:fuchsia.buildinfo_hlcpp", - "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.hwinfo:fuchsia.hwinfo_cpp", - "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.intl:fuchsia.intl_hlcpp", - "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.logger:fuchsia.logger_cpp", - "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.mem:fuchsia.mem_hlcpp", - "//third_party/fuchsia-sdk/sdk/pkg/async", - "//third_party/fuchsia-sdk/sdk/pkg/async-default", - "//third_party/fuchsia-sdk/sdk/pkg/component_incoming_cpp", - "//third_party/fuchsia-sdk/sdk/pkg/fdio", - "//third_party/fuchsia-sdk/sdk/pkg/sys_cpp", - ] - - additional_manifest_fragments = - [ "//build/config/fuchsia/test/logger.shard.test-cml" ] - } - - if (!use_cobalt_customizations && !is_fuchsia && !is_ios) { - sources += [ "files/file_locking_unittest.cc" ] - } - - if (use_cobalt_customizations) { - sources -= [ "files/file_path_watcher_unittest.cc" ] - } - - if (is_android && !use_cobalt_customizations) { - deps += [ "//testing/android/native_test:native_test_native_code" ] - sources += [ - "debug/elf_reader_unittest.cc", - "debug/proc_maps_linux_unittest.cc", - "debug/test_elf_image_builder.cc", - "debug/test_elf_image_builder.h", - ] - } - - if (is_win && !is_starboard) { - deps += [ "//base:scoped_handle_test_dll" ] - if (current_cpu == "x64" || current_cpu == "arm64") { - sources += [ "profiler/win32_stack_frame_unwinder_unittest.cc" ] - deps += [ ":base_profiler_test_support_library" ] - } - sources += [ "files/os_validation_win_unittest.cc" ] - - if (toolchain_has_rust && win_enable_cfg_guards) { - deps += [ ":rust_cfg_win_test" ] - } - } - if (is_apple && !is_starboard) { - sources += [ "profiler/frame_pointer_unwinder_unittest.cc" ] - } - if (is_chromeos && current_cpu == "x64") { - sources += [ "profiler/frame_pointer_unwinder_unittest.cc" ] - deps += [ ":base_profiler_test_support_library" ] - } - - if (use_allocator_shim) { - sources += [ - "allocator/partition_allocator/shim/allocator_shim_unittest.cc", - "sampling_heap_profiler/poisson_allocation_sampler_unittest.cc", - "sampling_heap_profiler/sampling_heap_profiler_unittest.cc", - ] - - if (is_win) { - sources += - [ "allocator/partition_allocator/shim/winheap_stubs_win_unittest.cc" ] - } - - sources += [ - "allocator/partition_alloc_support_unittest.cc", - "allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_partition_alloc_unittest.cc", - ] - } - - if (enable_base_tracing) { - sources += [ - "test/trace_event_analyzer_unittest.cc", - "trace_event/memory_allocator_dump_unittest.cc", - "trace_event/memory_dump_manager_unittest.cc", - "trace_event/memory_dump_scheduler_unittest.cc", - "trace_event/memory_infra_background_allowlist_unittest.cc", - "trace_event/memory_usage_estimator_unittest.cc", - "trace_event/process_memory_dump_unittest.cc", - "trace_event/trace_arguments_unittest.cc", - "trace_event/trace_category_unittest.cc", - "trace_event/trace_config_unittest.cc", - "trace_event/trace_conversion_helper_unittest.cc", - "trace_event/trace_event_unittest.cc", - "trace_event/traced_value_support_unittest.cc", - "trace_event/traced_value_unittest.cc", - "trace_event/typed_macros_unittest.cc", - "tracing/perfetto_task_runner_unittest.cc", - ] - - if (cobalt_pending_clean_up) { - sources -= [ - "trace_event/memory_allocator_dump_unittest.cc", - "trace_event/process_memory_dump_unittest.cc", - "trace_event/typed_macros_unittest.cc", - ] - } - - if (is_android && !is_starboard) { - sources += [ - "trace_event/cpufreq_monitor_android_unittest.cc", - "trace_event/java_heap_dump_provider_android_unittest.cc", - ] - } - - deps += [ - "//third_party/perfetto/include/perfetto/trace_processor:storage", - "//third_party/perfetto/protos/perfetto/trace:lite", - "//third_party/perfetto/protos/perfetto/trace/interned_data:lite", - "//third_party/perfetto/protos/perfetto/trace/track_event:lite", - ] - } -} - -# Test that CFG is enabled in Rust code. -if (is_win && toolchain_has_rust) { - source_set("rust_cfg_win_test") { - testonly = true - deps = [ - ":base", - "//testing/gtest", - ] - data_deps = [ "//build/rust/tests/test_control_flow_guard" ] - sources = [ "memory/rust_cfg_win_unittest.cc" ] - } -} - -action("build_date") { - script = "write_build_date_header.py" - - outputs = [ "$target_gen_dir/generated_build_date.h" ] - - args = [ - rebase_path("$target_gen_dir/generated_build_date.h", root_build_dir), - build_timestamp, - ] -} - -if (enable_nocompile_tests) { - nocompile_test("base_nocompile_tests") { - sources = [ - "allocator/partition_allocator/partition_alloc_base/thread_annotations_pa_unittest.nc", - "allocator/partition_allocator/pointers/raw_ptr_unittest.nc", - "allocator/partition_allocator/pointers/raw_ref_unittest.nc", - "callback_list_unittest.nc", - "containers/buffer_iterator_unittest.nc", - "containers/checked_iterators_unittest.nc", - "containers/contains_unittest.nc", - "containers/enum_set_unittest.nc", - "containers/span_unittest.nc", - "debug/crash_logging_unittest.nc", - "functional/bind_unittest.nc", - "functional/callback_unittest.nc", - "functional/function_ref_unittest.nc", - "functional/overloaded_unittest.nc", - "memory/ref_counted_unittest.nc", - "memory/weak_ptr_unittest.nc", - "metrics/field_trial_params_unittest.nc", - "metrics/histogram_unittest.nc", - "no_destructor_unittest.nc", - "observer_list_unittest.nc", - "sequence_checker_unittest.nc", - "strings/string_piece_unittest.nc", - "task/bind_post_task_unittest.nc", - "task/task_traits_unittest.nc", - "thread_annotations_unittest.nc", - "traits_bag_unittest.nc", - "types/pass_key_unittest.nc", - "types/variant_util_unittest.nc", - "values_unittest.nc", - ] - - deps = [ - ":base", - "//base/test:run_all_unittests", - "//testing/gtest", - ] - - # Since the nocompile test is not able to inspect the contents of - # include_dirs, we need to specify the Abseil include dir manually. - include_dirs = [ "//third_party/abseil-cpp" ] - } -} - -if ((is_android || is_robolectric) && !use_cobalt_customizations) { - generate_jni("base_jni_headers") { - sources = [ - "android/java/src/org/chromium/base/ApkAssets.java", - "android/java/src/org/chromium/base/ApplicationStatus.java", - "android/java/src/org/chromium/base/BaseFeatureList.java", - "android/java/src/org/chromium/base/BuildInfo.java", - "android/java/src/org/chromium/base/BundleUtils.java", - "android/java/src/org/chromium/base/Callback.java", - "android/java/src/org/chromium/base/CommandLine.java", - "android/java/src/org/chromium/base/ContentUriUtils.java", - "android/java/src/org/chromium/base/CpuFeatures.java", - "android/java/src/org/chromium/base/EarlyTraceEvent.java", - "android/java/src/org/chromium/base/EventLog.java", - "android/java/src/org/chromium/base/FeatureList.java", - "android/java/src/org/chromium/base/Features.java", - "android/java/src/org/chromium/base/FieldTrialList.java", - "android/java/src/org/chromium/base/FileUtils.java", - "android/java/src/org/chromium/base/ImportantFileWriterAndroid.java", - "android/java/src/org/chromium/base/IntStringCallback.java", - "android/java/src/org/chromium/base/JNIUtils.java", - "android/java/src/org/chromium/base/JavaExceptionReporter.java", - "android/java/src/org/chromium/base/JavaHandlerThread.java", - "android/java/src/org/chromium/base/LocaleUtils.java", - "android/java/src/org/chromium/base/MemoryPressureListener.java", - "android/java/src/org/chromium/base/PathService.java", - "android/java/src/org/chromium/base/PathUtils.java", - "android/java/src/org/chromium/base/PiiElider.java", - "android/java/src/org/chromium/base/PowerMonitor.java", - "android/java/src/org/chromium/base/RadioUtils.java", - "android/java/src/org/chromium/base/SysUtils.java", - "android/java/src/org/chromium/base/ThreadUtils.java", - "android/java/src/org/chromium/base/TimezoneUtils.java", - "android/java/src/org/chromium/base/TraceEvent.java", - "android/java/src/org/chromium/base/UnguessableToken.java", - "android/java/src/org/chromium/base/library_loader/LibraryLoader.java", - "android/java/src/org/chromium/base/library_loader/LibraryPrefetcher.java", - "android/java/src/org/chromium/base/memory/JavaHeapDumpGenerator.java", - "android/java/src/org/chromium/base/memory/MemoryInfoBridge.java", - "android/java/src/org/chromium/base/metrics/NativeUmaRecorder.java", - "android/java/src/org/chromium/base/metrics/StatisticsRecorderAndroid.java", - "android/java/src/org/chromium/base/process_launcher/ChildProcessService.java", - "android/java/src/org/chromium/base/task/PostTask.java", - "android/java/src/org/chromium/base/task/TaskRunnerImpl.java", - ] - - public_deps = [ - ":android_runtime_jni_headers", - ":android_runtime_unchecked_jni_headers", - ] - } - - generate_jar_jni("android_runtime_jni_headers") { - classes = [ "java/lang/Runtime.class" ] - } - - generate_jar_jni("android_runtime_unchecked_jni_headers") { - classes = [ "java/lang/Runnable.class" ] - unchecked_exceptions = true - } -} # is_android || is_robolectric - -if (is_android && !use_cobalt_customizations) { - java_library("jni_java") { - supports_android = true - sources = [ - "android/java/src/org/chromium/base/JniException.java", - "android/java/src/org/chromium/base/JniStaticTestMocker.java", - "android/java/src/org/chromium/base/NativeLibraryLoadedStatus.java", - "android/java/src/org/chromium/base/annotations/AccessedByNative.java", - "android/java/src/org/chromium/base/annotations/CalledByNative.java", - "android/java/src/org/chromium/base/annotations/CalledByNativeForTesting.java", - "android/java/src/org/chromium/base/annotations/CalledByNativeUnchecked.java", - "android/java/src/org/chromium/base/annotations/JNIAdditionalImport.java", - "android/java/src/org/chromium/base/annotations/JNINamespace.java", - "android/java/src/org/chromium/base/annotations/JniIgnoreNatives.java", - "android/java/src/org/chromium/base/annotations/NativeClassQualifiedName.java", - "android/java/src/org/chromium/base/annotations/NativeMethods.java", - ] - - # Public because @CheckDiscard is added to generated *Jni.java files. - public_deps = [ "//build/android:build_java" ] - } - - java_cpp_features("java_features_srcjar") { - # External code should depend on ":base_java" instead. - visibility = [ ":*" ] - sources = [ - "android/base_features.cc", - "features.cc", - "task/task_features.cc", - ] - template = "android/java/src/org/chromium/base/BaseFeatures.java.tmpl" - } - - java_cpp_strings("java_switches_srcjar") { - # External code should depend on ":base_java" instead. - visibility = [ ":*" ] - sources = [ "base_switches.cc" ] - template = "android/java/src/org/chromium/base/BaseSwitches.java.tmpl" - } - - android_library("process_launcher_java") { - srcjar_deps = [ ":process_launcher_aidl" ] - deps = [ - ":base_java", - ":jni_java", - "//build/android:build_java", - "//third_party/android_deps:com_google_code_findbugs_jsr305_java", - "//third_party/androidx:androidx_annotation_annotation_experimental_java", - "//third_party/androidx:androidx_annotation_annotation_java", - "//third_party/androidx:androidx_collection_collection_java", - "//third_party/androidx:androidx_core_core_java", - ] - sources = [ - "android/java/src/org/chromium/base/process_launcher/BindService.java", - "android/java/src/org/chromium/base/process_launcher/ChildConnectionAllocator.java", - "android/java/src/org/chromium/base/process_launcher/ChildProcessConnection.java", - "android/java/src/org/chromium/base/process_launcher/ChildProcessConstants.java", - "android/java/src/org/chromium/base/process_launcher/ChildProcessLauncher.java", - "android/java/src/org/chromium/base/process_launcher/ChildProcessService.java", - "android/java/src/org/chromium/base/process_launcher/ChildProcessServiceDelegate.java", - "android/java/src/org/chromium/base/process_launcher/ChildServiceConnection.java", - "android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionDelegate.java", - "android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionFactory.java", - "android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionImpl.java", - "android/java/src/org/chromium/base/process_launcher/FileDescriptorInfo.java", - ] - - annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] - } - - android_library("base_java") { - srcjar_deps = [ - ":base_android_java_enums_srcjar", - ":java_features_srcjar", - ":java_switches_srcjar", - ] - - deps = [ - ":jni_java", - "//build/android:build_java", - "//third_party/android_deps:com_google_code_findbugs_jsr305_java", - "//third_party/android_deps:com_google_errorprone_error_prone_annotations_java", - "//third_party/androidx:androidx_annotation_annotation_experimental_java", - "//third_party/androidx:androidx_annotation_annotation_java", - "//third_party/androidx:androidx_collection_collection_java", - "//third_party/androidx:androidx_core_core_java", - ] - - sources = [ - "android/java/src/org/chromium/base/ActivityState.java", - "android/java/src/org/chromium/base/ApiCompatibilityUtils.java", - "android/java/src/org/chromium/base/ApkAssets.java", - "android/java/src/org/chromium/base/ApplicationStatus.java", - "android/java/src/org/chromium/base/BaseFeatureList.java", - "android/java/src/org/chromium/base/BuildInfo.java", - "android/java/src/org/chromium/base/BundleUtils.java", - "android/java/src/org/chromium/base/Callback.java", - "android/java/src/org/chromium/base/CallbackController.java", - "android/java/src/org/chromium/base/CollectionUtil.java", - "android/java/src/org/chromium/base/CommandLine.java", - "android/java/src/org/chromium/base/CommandLineInitUtil.java", - "android/java/src/org/chromium/base/ContentUriUtils.java", - "android/java/src/org/chromium/base/ContextUtils.java", - "android/java/src/org/chromium/base/CpuFeatures.java", - "android/java/src/org/chromium/base/DiscardableReferencePool.java", - "android/java/src/org/chromium/base/EarlyTraceEvent.java", - "android/java/src/org/chromium/base/EventLog.java", - "android/java/src/org/chromium/base/FeatureList.java", - "android/java/src/org/chromium/base/Features.java", - "android/java/src/org/chromium/base/FieldTrialList.java", - "android/java/src/org/chromium/base/FileUtils.java", - "android/java/src/org/chromium/base/Flag.java", - "android/java/src/org/chromium/base/ImportantFileWriterAndroid.java", - "android/java/src/org/chromium/base/IntStringCallback.java", - "android/java/src/org/chromium/base/JNIUtils.java", - "android/java/src/org/chromium/base/JavaExceptionReporter.java", - "android/java/src/org/chromium/base/JavaHandlerThread.java", - "android/java/src/org/chromium/base/LifetimeAssert.java", - "android/java/src/org/chromium/base/LocaleUtils.java", - "android/java/src/org/chromium/base/Log.java", - "android/java/src/org/chromium/base/MathUtils.java", - "android/java/src/org/chromium/base/MemoryPressureListener.java", - "android/java/src/org/chromium/base/ObserverList.java", - "android/java/src/org/chromium/base/PackageManagerUtils.java", - "android/java/src/org/chromium/base/PackageUtils.java", - "android/java/src/org/chromium/base/PathService.java", - "android/java/src/org/chromium/base/PathUtils.java", - "android/java/src/org/chromium/base/PiiElider.java", - "android/java/src/org/chromium/base/PowerMonitor.java", - "android/java/src/org/chromium/base/PowerMonitorForQ.java", - "android/java/src/org/chromium/base/Promise.java", - "android/java/src/org/chromium/base/RadioUtils.java", - "android/java/src/org/chromium/base/RequiredCallback.java", - "android/java/src/org/chromium/base/ResettersForTesting.java", - "android/java/src/org/chromium/base/StreamUtil.java", - "android/java/src/org/chromium/base/StrictModeContext.java", - "android/java/src/org/chromium/base/SysUtils.java", - "android/java/src/org/chromium/base/ThreadUtils.java", - "android/java/src/org/chromium/base/TimeUtils.java", - "android/java/src/org/chromium/base/TimezoneUtils.java", - "android/java/src/org/chromium/base/TraceEvent.java", - "android/java/src/org/chromium/base/UnguessableToken.java", - "android/java/src/org/chromium/base/UnownedUserData.java", - "android/java/src/org/chromium/base/UnownedUserDataHost.java", - "android/java/src/org/chromium/base/UnownedUserDataKey.java", - "android/java/src/org/chromium/base/UserData.java", - "android/java/src/org/chromium/base/UserDataHost.java", - "android/java/src/org/chromium/base/WrappedClassLoader.java", - "android/java/src/org/chromium/base/compat/ApiHelperForM.java", - "android/java/src/org/chromium/base/compat/ApiHelperForN.java", - "android/java/src/org/chromium/base/compat/ApiHelperForO.java", - "android/java/src/org/chromium/base/compat/ApiHelperForOMR1.java", - "android/java/src/org/chromium/base/compat/ApiHelperForP.java", - "android/java/src/org/chromium/base/compat/ApiHelperForQ.java", - "android/java/src/org/chromium/base/compat/ApiHelperForR.java", - "android/java/src/org/chromium/base/compat/ApiHelperForS.java", - "android/java/src/org/chromium/base/library_loader/LibraryLoader.java", - "android/java/src/org/chromium/base/library_loader/LibraryPrefetcher.java", - "android/java/src/org/chromium/base/library_loader/Linker.java", - "android/java/src/org/chromium/base/library_loader/LinkerJni.java", - "android/java/src/org/chromium/base/library_loader/LoaderErrors.java", - "android/java/src/org/chromium/base/library_loader/NativeLibraryPreloader.java", - "android/java/src/org/chromium/base/library_loader/ProcessInitException.java", - "android/java/src/org/chromium/base/lifetime/DestroyChecker.java", - "android/java/src/org/chromium/base/lifetime/Destroyable.java", - "android/java/src/org/chromium/base/memory/JavaHeapDumpGenerator.java", - "android/java/src/org/chromium/base/memory/MemoryInfoBridge.java", - "android/java/src/org/chromium/base/memory/MemoryPressureCallback.java", - "android/java/src/org/chromium/base/memory/MemoryPressureMonitor.java", - "android/java/src/org/chromium/base/memory/MemoryPressureUma.java", - "android/java/src/org/chromium/base/memory/MemoryPurgeManager.java", - "android/java/src/org/chromium/base/metrics/CachingUmaRecorder.java", - "android/java/src/org/chromium/base/metrics/HistogramBucket.java", - "android/java/src/org/chromium/base/metrics/NativeUmaRecorder.java", - "android/java/src/org/chromium/base/metrics/NoopUmaRecorder.java", - "android/java/src/org/chromium/base/metrics/RecordHistogram.java", - "android/java/src/org/chromium/base/metrics/RecordUserAction.java", - "android/java/src/org/chromium/base/metrics/ScopedSysTraceEvent.java", - "android/java/src/org/chromium/base/metrics/StatisticsRecorderAndroid.java", - "android/java/src/org/chromium/base/metrics/TimingMetric.java", - "android/java/src/org/chromium/base/metrics/UmaRecorder.java", - "android/java/src/org/chromium/base/metrics/UmaRecorderHolder.java", - "android/java/src/org/chromium/base/supplier/DestroyableObservableSupplier.java", - "android/java/src/org/chromium/base/supplier/ObservableSupplier.java", - "android/java/src/org/chromium/base/supplier/ObservableSupplierImpl.java", - "android/java/src/org/chromium/base/supplier/OneShotCallback.java", - "android/java/src/org/chromium/base/supplier/OneshotSupplier.java", - "android/java/src/org/chromium/base/supplier/OneshotSupplierImpl.java", - "android/java/src/org/chromium/base/supplier/Supplier.java", - "android/java/src/org/chromium/base/supplier/UnownedUserDataSupplier.java", - "android/java/src/org/chromium/base/task/AsyncTask.java", - "android/java/src/org/chromium/base/task/BackgroundOnlyAsyncTask.java", - "android/java/src/org/chromium/base/task/ChainedTasks.java", - "android/java/src/org/chromium/base/task/ChromeThreadPoolExecutor.java", - "android/java/src/org/chromium/base/task/PostTask.java", - "android/java/src/org/chromium/base/task/SequencedTaskRunner.java", - "android/java/src/org/chromium/base/task/SequencedTaskRunnerImpl.java", - "android/java/src/org/chromium/base/task/SerialExecutor.java", - "android/java/src/org/chromium/base/task/SingleThreadTaskRunner.java", - "android/java/src/org/chromium/base/task/SingleThreadTaskRunnerImpl.java", - "android/java/src/org/chromium/base/task/TaskExecutor.java", - "android/java/src/org/chromium/base/task/TaskRunner.java", - "android/java/src/org/chromium/base/task/TaskRunnerImpl.java", - "android/java/src/org/chromium/base/task/ThreadPoolTaskExecutor.java", - "android/java/src/org/chromium/base/task/UiThreadTaskExecutor.java", - ] - - if (!is_cronet_build) { - sources += [ "android/java/src/org/chromium/base/IntentUtils.java" ] - } - - annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] - - resources_package = "org.chromium.base" - - proguard_configs = [ - "android/proguard/chromium_apk.flags", - "android/proguard/chromium_code.flags", - "android/proguard/remove_logging.flags", - ] - } - - android_aidl("process_launcher_aidl") { - import_include = [ "android/java/src" ] - sources = [ - "android/java/src/org/chromium/base/process_launcher/IChildProcessService.aidl", - "android/java/src/org/chromium/base/process_launcher/IParentProcess.aidl", - ] - } - - android_library("base_javatests") { - testonly = true - - deps = [ - ":base_java", - ":base_java_test_support", - ":base_java_test_support_uncommon", - "//base:jni_java", - "//base/test:test_support_java", - "//build/android:build_java", - "//third_party/androidx:androidx_annotation_annotation_java", - "//third_party/androidx:androidx_test_monitor_java", - "//third_party/androidx:androidx_test_runner_java", - "//third_party/hamcrest:hamcrest_java", - "//third_party/junit:junit", - "//third_party/mockito:mockito_java", - ] - - sources = [ - # AssertsTest doesn't really belong in //base but it's preferable to - # stick it here than create another target for a single test. - "android/javatests/src/org/chromium/base/AdvancedMockContextTest.java", - "android/javatests/src/org/chromium/base/AssertsTest.java", - "android/javatests/src/org/chromium/base/CommandLineFlagsTest.java", - "android/javatests/src/org/chromium/base/CommandLineInitUtilTest.java", - "android/javatests/src/org/chromium/base/CommandLineTest.java", - "android/javatests/src/org/chromium/base/EarlyTraceEventTest.java", - "android/javatests/src/org/chromium/base/IntentUtilsTest.java", - "android/javatests/src/org/chromium/base/LocaleUtilsTest.java", - "android/javatests/src/org/chromium/base/ObserverListTest.java", - "android/javatests/src/org/chromium/base/PackageUtilsTest.java", - "android/javatests/src/org/chromium/base/StrictModeContextTest.java", - "android/javatests/src/org/chromium/base/UserDataHostTest.java", - "android/javatests/src/org/chromium/base/library_loader/EarlyNativeTest.java", - "android/javatests/src/org/chromium/base/metrics/RecordHistogramTest.java", - "android/javatests/src/org/chromium/base/task/AsyncTaskTest.java", - "android/javatests/src/org/chromium/base/task/PostTaskTest.java", - "android/javatests/src/org/chromium/base/task/SequencedTaskRunnerImplTest.java", - "android/javatests/src/org/chromium/base/task/SingleThreadTaskRunnerImplTest.java", - "android/javatests/src/org/chromium/base/task/TaskRunnerImplTest.java", - "android/javatests/src/org/chromium/base/test/metrics/HistogramWatcherTestBase.java", - "android/javatests/src/org/chromium/base/test/metrics/HistogramWatcherTransitionToNativeTest.java", - "android/javatests/src/org/chromium/base/test/metrics/HistogramWatcherWithNativeTest.java", - "android/javatests/src/org/chromium/base/test/metrics/HistogramWatcherWithoutNativeTest.java", - "android/javatests/src/org/chromium/base/util/GarbageCollectionTestUtilsTest.java", - ] - - annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] - } - - source_set("base_javatests_lib") { - testonly = true - deps = [ - ":base", - ":base_javatests_jni_headers", - ] - sources = [ "test/library_loader/early_native_test_helper.cc" ] - } - - generate_jni("base_javatests_jni_headers") { - testonly = true - sources = [ "android/javatests/src/org/chromium/base/library_loader/EarlyNativeTest.java" ] - } - - android_library("base_java_url_utils_for_test") { - testonly = true - sources = [ - "test/android/javatests/src/org/chromium/base/test/util/UrlUtils.java", - ] - - deps = [ - ":base_java", - "//base:jni_java", - "//build/android:build_java", - "//third_party/androidx:androidx_annotation_annotation_java", - "//third_party/junit:junit", - ] - annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] - } - - android_library("base_java_test_support") { - testonly = true - - deps = [ - ":base_java", - "//base:jni_java", - "//build/android:build_java", - "//third_party/accessibility_test_framework:accessibility_test_framework_java", - "//third_party/android_deps:espresso_java", - "//third_party/android_deps:guava_android_java", - "//third_party/android_sdk:android_test_base_java", - "//third_party/android_sdk:android_test_mock_java", - "//third_party/androidx:androidx_annotation_annotation_java", - "//third_party/androidx:androidx_core_core_java", - "//third_party/androidx:androidx_multidex_multidex_java", - "//third_party/androidx:androidx_test_monitor_java", - "//third_party/androidx:androidx_test_runner_java", - "//third_party/androidx:androidx_test_uiautomator_uiautomator_java", - "//third_party/hamcrest:hamcrest_java", - "//third_party/junit", - ] - - public_deps = [ ":base_java_url_utils_for_test" ] - - sources = [ - "test/android/java/src/org/chromium/base/multidex/ChromiumMultiDexInstaller.java", - "test/android/javatests/src/org/chromium/base/test/BaseActivityTestRule.java", - "test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java", - "test/android/javatests/src/org/chromium/base/test/BaseChromiumRunnerCommon.java", - "test/android/javatests/src/org/chromium/base/test/BaseJUnit4ClassRunner.java", - "test/android/javatests/src/org/chromium/base/test/BaseJUnit4TestRule.java", - "test/android/javatests/src/org/chromium/base/test/LoadNative.java", - "test/android/javatests/src/org/chromium/base/test/MockitoErrorHandler.java", - "test/android/javatests/src/org/chromium/base/test/ResetCachedFlagValuesTestHook.java", - "test/android/javatests/src/org/chromium/base/test/ResettersForTestingTestRule.java", - "test/android/javatests/src/org/chromium/base/test/ScreenshotOnFailureStatement.java", - "test/android/javatests/src/org/chromium/base/test/TestListInstrumentationRunListener.java", - "test/android/javatests/src/org/chromium/base/test/TestTraceEvent.java", - "test/android/javatests/src/org/chromium/base/test/UiThreadStatement.java", - "test/android/javatests/src/org/chromium/base/test/UiThreadTest.java", - "test/android/javatests/src/org/chromium/base/test/UnitTestLifetimeAssertRule.java", - "test/android/javatests/src/org/chromium/base/test/UnitTestNoBrowserProcessHook.java", - "test/android/javatests/src/org/chromium/base/test/params/BaseJUnit4RunnerDelegate.java", - "test/android/javatests/src/org/chromium/base/test/params/BlockJUnit4RunnerDelegate.java", - "test/android/javatests/src/org/chromium/base/test/params/MethodParamAnnotationRule.java", - "test/android/javatests/src/org/chromium/base/test/params/MethodParamRule.java", - "test/android/javatests/src/org/chromium/base/test/params/ParameterAnnotations.java", - "test/android/javatests/src/org/chromium/base/test/params/ParameterProvider.java", - "test/android/javatests/src/org/chromium/base/test/params/ParameterSet.java", - "test/android/javatests/src/org/chromium/base/test/params/ParameterizedCommandLineFlags.java", - "test/android/javatests/src/org/chromium/base/test/params/ParameterizedFrameworkMethod.java", - "test/android/javatests/src/org/chromium/base/test/params/ParameterizedRunner.java", - "test/android/javatests/src/org/chromium/base/test/params/ParameterizedRunnerDelegate.java", - "test/android/javatests/src/org/chromium/base/test/params/ParameterizedRunnerDelegateCommon.java", - "test/android/javatests/src/org/chromium/base/test/params/ParameterizedRunnerDelegateFactory.java", - "test/android/javatests/src/org/chromium/base/test/params/SkipCommandLineParameterization.java", - "test/android/javatests/src/org/chromium/base/test/util/AdvancedMockContext.java", - "test/android/javatests/src/org/chromium/base/test/util/AndroidSdkLevelSkipCheck.java", - "test/android/javatests/src/org/chromium/base/test/util/AnnotationProcessingUtils.java", - "test/android/javatests/src/org/chromium/base/test/util/AnnotationRule.java", - "test/android/javatests/src/org/chromium/base/test/util/ApplicationContextWrapper.java", - "test/android/javatests/src/org/chromium/base/test/util/ApplicationTestUtils.java", - "test/android/javatests/src/org/chromium/base/test/util/Batch.java", - "test/android/javatests/src/org/chromium/base/test/util/CallbackHelper.java", - "test/android/javatests/src/org/chromium/base/test/util/CommandLineFlags.java", - "test/android/javatests/src/org/chromium/base/test/util/Criteria.java", - "test/android/javatests/src/org/chromium/base/test/util/CriteriaHelper.java", - "test/android/javatests/src/org/chromium/base/test/util/CriteriaNotSatisfiedException.java", - "test/android/javatests/src/org/chromium/base/test/util/DisableIf.java", - "test/android/javatests/src/org/chromium/base/test/util/DisableIfSkipCheck.java", - "test/android/javatests/src/org/chromium/base/test/util/DisabledTest.java", - "test/android/javatests/src/org/chromium/base/test/util/DoNotBatch.java", - "test/android/javatests/src/org/chromium/base/test/util/DoNotRevive.java", - "test/android/javatests/src/org/chromium/base/test/util/EnormousTest.java", - "test/android/javatests/src/org/chromium/base/test/util/Feature.java", - "test/android/javatests/src/org/chromium/base/test/util/Features.java", - "test/android/javatests/src/org/chromium/base/test/util/FeaturesBase.java", - "test/android/javatests/src/org/chromium/base/test/util/HistogramWatcher.java", - "test/android/javatests/src/org/chromium/base/test/util/InMemorySharedPreferences.java", - "test/android/javatests/src/org/chromium/base/test/util/InMemorySharedPreferencesContext.java", - "test/android/javatests/src/org/chromium/base/test/util/IntegrationTest.java", - "test/android/javatests/src/org/chromium/base/test/util/JniMocker.java", - "test/android/javatests/src/org/chromium/base/test/util/LooperUtils.java", - "test/android/javatests/src/org/chromium/base/test/util/Manual.java", - "test/android/javatests/src/org/chromium/base/test/util/MaxAndroidSdkLevel.java", - "test/android/javatests/src/org/chromium/base/test/util/MinAndroidSdkLevel.java", - "test/android/javatests/src/org/chromium/base/test/util/PackageManagerWrapper.java", - "test/android/javatests/src/org/chromium/base/test/util/PayloadCallbackHelper.java", - "test/android/javatests/src/org/chromium/base/test/util/RequiresRestart.java", - "test/android/javatests/src/org/chromium/base/test/util/Restriction.java", - "test/android/javatests/src/org/chromium/base/test/util/RestrictionSkipCheck.java", - "test/android/javatests/src/org/chromium/base/test/util/ScalableTimeout.java", - "test/android/javatests/src/org/chromium/base/test/util/SkipCheck.java", - "test/android/javatests/src/org/chromium/base/test/util/TestFileUtil.java", - "test/android/javatests/src/org/chromium/base/test/util/TestThreadUtils.java", - "test/android/javatests/src/org/chromium/base/test/util/TimeoutScale.java", - "test/android/javatests/src/org/chromium/base/test/util/TimeoutTimer.java", - "test/android/javatests/src/org/chromium/base/test/util/UserActionTester.java", - ] - annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] - } - - android_library("base_java_test_support_uncommon") { - testonly = true - - deps = [ - "//base:base_java", - "//base:jni_java", - "//third_party/androidx:androidx_annotation_annotation_java", - "//third_party/androidx:androidx_test_monitor_java", - "//third_party/hamcrest:hamcrest_java", - "//third_party/junit:junit", - ] - - sources = [ - "test/android/javatests/src/org/chromium/base/FakeTimeTestRule.java", - "test/android/javatests/src/org/chromium/base/test/BundleTestRule.java", - "test/android/javatests/src/org/chromium/base/test/ReachedCodeProfiler.java", - "test/android/javatests/src/org/chromium/base/test/SetUpStatement.java", - "test/android/javatests/src/org/chromium/base/test/SetUpTestRule.java", - "test/android/javatests/src/org/chromium/base/test/task/SchedulerTestHelpers.java", - "test/android/javatests/src/org/chromium/base/test/task/ThreadPoolTestHelpers.java", - "test/android/javatests/src/org/chromium/base/test/util/CloseableOnMainThread.java", - "test/android/javatests/src/org/chromium/base/test/util/InstrumentationUtils.java", - "test/android/javatests/src/org/chromium/base/test/util/Matchers.java", - ] - - annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] - } - - android_library("base_java_process_launcher_test_support") { - testonly = true - - public_deps = [ ":process_launcher_java" ] - - sources = [ "test/android/javatests/src/org/chromium/base/process_launcher/TestChildProcessConnection.java" ] - } - - robolectric_library("base_junit_test_support") { - sources = [ - "//third_party/robolectric/custom_asynctask/java/src/org/chromium/base/task/test/ShadowAsyncTask.java", - "//third_party/robolectric/custom_asynctask/java/src/org/chromium/base/task/test/ShadowAsyncTaskBridge.java", - "test/android/junit/src/org/chromium/base/task/test/BackgroundShadowAsyncTask.java", - "test/android/junit/src/org/chromium/base/task/test/CustomShadowAsyncTask.java", - "test/android/junit/src/org/chromium/base/task/test/PausedExecutorTestRule.java", - "test/android/junit/src/org/chromium/base/task/test/ShadowPostTask.java", - "test/android/junit/src/org/chromium/base/test/BaseRobolectricTestRunner.java", - "test/android/junit/src/org/chromium/base/test/ShadowBuildInfo.java", - "test/android/junit/src/org/chromium/base/test/util/TestRunnerTestRule.java", - ] - - # Make sure robolectric tests have classes filtered out of base_java by - # jar_excluded_patterns. - srcjar_deps = [ "//build/android:native_libraries_gen" ] - - deps = [ - ":base_java", - ":base_java_test_support", - "//testing/android/junit:junit_test_support", - "//third_party/androidx:androidx_test_core_java", - "//third_party/androidx:androidx_test_monitor_java", - "//third_party/hamcrest:hamcrest_java", - "//third_party/junit:junit", - ] - } - - robolectric_binary("base_junit_tests") { - sources = [ - "android/junit/src/org/chromium/base/ApplicationStatusTest.java", - "android/junit/src/org/chromium/base/BuildInfoTest.java", - "android/junit/src/org/chromium/base/CallbackControllerTest.java", - "android/junit/src/org/chromium/base/CollectionUtilTest.java", - "android/junit/src/org/chromium/base/DiscardableReferencePoolTest.java", - "android/junit/src/org/chromium/base/FileUtilsTest.java", - "android/junit/src/org/chromium/base/LifetimeAssertTest.java", - "android/junit/src/org/chromium/base/LogTest.java", - "android/junit/src/org/chromium/base/MathUtilsTest.java", - "android/junit/src/org/chromium/base/PathUtilsTest.java", - "android/junit/src/org/chromium/base/PiiEliderTest.java", - "android/junit/src/org/chromium/base/PromiseTest.java", - "android/junit/src/org/chromium/base/ResettersForTestingTest.java", - "android/junit/src/org/chromium/base/TimeUtilsTest.java", - "android/junit/src/org/chromium/base/TraceEventTest.java", - "android/junit/src/org/chromium/base/UnownedUserDataHostTest.java", - "android/junit/src/org/chromium/base/UnownedUserDataKeyTest.java", - "android/junit/src/org/chromium/base/library_loader/LinkerTest.java", - "android/junit/src/org/chromium/base/memory/MemoryPressureMonitorTest.java", - "android/junit/src/org/chromium/base/memory/MemoryPurgeManagerTest.java", - "android/junit/src/org/chromium/base/metrics/CachingUmaRecorderTest.java", - "android/junit/src/org/chromium/base/process_launcher/ChildConnectionAllocatorTest.java", - "android/junit/src/org/chromium/base/process_launcher/ChildProcessConnectionTest.java", - "android/junit/src/org/chromium/base/supplier/ObservableSupplierImplTest.java", - "android/junit/src/org/chromium/base/supplier/OneShotCallbackTest.java", - "android/junit/src/org/chromium/base/supplier/OneshotSupplierImplTest.java", - "android/junit/src/org/chromium/base/supplier/UnownedUserDataSupplierTest.java", - "android/junit/src/org/chromium/base/task/AsyncTaskThreadTest.java", - "android/junit/src/org/chromium/base/task/SequencedTaskRunnerTaskMigrationTest.java", - "android/junit/src/org/chromium/base/util/GarbageCollectionTestUtilsUnitTest.java", - "test/android/junit/src/org/chromium/base/test/SetUpStatementTest.java", - "test/android/junit/src/org/chromium/base/test/TestListInstrumentationRunListenerTest.java", - "test/android/junit/src/org/chromium/base/test/params/ExampleParameterizedTest.java", - "test/android/junit/src/org/chromium/base/test/params/ParameterizedRunnerDelegateCommonTest.java", - "test/android/junit/src/org/chromium/base/test/params/ParameterizedRunnerDelegateFactoryTest.java", - "test/android/junit/src/org/chromium/base/test/params/ParameterizedRunnerTest.java", - "test/android/junit/src/org/chromium/base/test/params/ParameterizedTestNameTest.java", - "test/android/junit/src/org/chromium/base/test/util/AndroidSdkLevelSkipCheckTest.java", - "test/android/junit/src/org/chromium/base/test/util/AnnotationProcessingUtilsTest.java", - "test/android/junit/src/org/chromium/base/test/util/CommandLineFlagsNoClassAnnotationCheckTest.java", - "test/android/junit/src/org/chromium/base/test/util/CommandLineFlagsWithClassAnnotationCheckTest.java", - "test/android/junit/src/org/chromium/base/test/util/DisableIfTest.java", - "test/android/junit/src/org/chromium/base/test/util/DisabledTestForClassRobolectricTest.java", - "test/android/junit/src/org/chromium/base/test/util/DisabledTestRobolectricTest.java", - "test/android/junit/src/org/chromium/base/test/util/RestrictionSkipCheckTest.java", - "test/android/junit/src/org/chromium/base/test/util/SkipCheckTest.java", - ] - deps = [ - ":base_java", - ":base_java_process_launcher_test_support", - ":base_java_test_support", - ":base_java_test_support_uncommon", - ":base_junit_test_support", - "//base/test:test_support_java", - "//third_party/androidx:androidx_annotation_annotation_java", - "//third_party/androidx:androidx_test_core_java", - "//third_party/androidx:androidx_test_runner_java", - "//third_party/hamcrest:hamcrest_java", - ] - } - - java_cpp_enum("base_android_java_enums_srcjar") { - sources = [ - "android/application_status_listener.h", - "android/child_process_binding_types.h", - "android/library_loader/library_loader_hooks.h", - "android/linker/linker_jni.h", - "android/task_scheduler/task_runner_android.h", - "android/task_scheduler/task_traits_android.h", - "memory/memory_pressure_listener.h", - "metrics/histogram_base.h", - "task/task_traits.h", - ] - if (enable_base_tracing) { - sources += [ "trace_event/trace_config.h" ] - } - } - - generate_jni("base_profiler_test_support_jni_headers") { - testonly = true - sources = - [ "android/javatests/src/org/chromium/base/profiler/TestSupport.java" ] - } - - android_library("base_profiler_test_support_java") { - testonly = true - sources = - [ "android/javatests/src/org/chromium/base/profiler/TestSupport.java" ] - - annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] - - deps = [ - "//base:jni_java", - "//build/android:build_java", - ] - } - - android_library("base_java_unittest_support") { - testonly = true - - deps = [ - ":base_java", - "//base:jni_java", - ] - - sources = [ - "test/android/java/src/org/chromium/base/ContentUriTestUtils.java", - "test/android/java/src/org/chromium/base/JavaHandlerThreadHelpers.java", - ] - } -} # is_android - -# Keep the list of fuzzer_tests in alphabetical order. -fuzzer_test("base64_decode_fuzzer") { - sources = [ "base64_decode_fuzzer.cc" ] - deps = [ "//base" ] -} - -fuzzer_test("base64_encode_fuzzer") { - sources = [ "base64_encode_fuzzer.cc" ] - deps = [ "//base" ] -} - -fuzzer_test("base64url_fuzzer") { - sources = [ "base64url_fuzzer.cc" ] - deps = [ "//base" ] -} - -fuzzer_test("base_json_correctness_fuzzer") { - sources = [ "json/json_correctness_fuzzer.cc" ] - deps = [ ":base" ] - dict = "//testing/libfuzzer/fuzzers/dicts/json.dict" -} - -fuzzer_test("base_json_reader_fuzzer") { - sources = [ "json/json_reader_fuzzer.cc" ] - deps = [ "//base" ] - dict = "//testing/libfuzzer/fuzzers/dicts/json.dict" -} - -fuzzer_test("base_json_string_escape_fuzzer") { - sources = [ "json/string_escape_fuzzer.cc" ] - deps = [ "//base" ] -} - -if (is_mac && !is_starboard) { - protoc_convert("base_mach_port_rendezvous_convert_corpus") { - sources = [ - "test/data/mach_port_rendezvous_fuzz/dead_name.textproto", - "test/data/mach_port_rendezvous_fuzz/send.textproto", - ] - inputs = [ "//testing/libfuzzer/fuzzers/mach/mach_message.proto" ] - output_pattern = "$target_gen_dir/base_mach_port_rendezvous_corpus/{{source_name_part}}.binarypb" - args = [ - "--encode=mach_fuzzer.MachMessage", - "-I", - rebase_path("//"), - rebase_path(inputs[0]), - ] - } - fuzzer_test("base_mach_port_rendezvous_fuzzer") { - sources = [ "mac/mach_port_rendezvous_fuzzer.cc" ] - deps = [ - "//base", - "//testing/libfuzzer/fuzzers/mach:converter", - "//third_party/libprotobuf-mutator", - ] - seed_corpus = "$target_gen_dir/base_mach_port_rendezvous_corpus" - seed_corpus_deps = [ ":base_mach_port_rendezvous_convert_corpus" ] - } -} - -fuzzer_test("command_line_fuzzer") { - sources = [ "command_line_fuzzer.cc" ] - deps = [ "//base" ] -} - -fuzzer_test("escape_fuzzer") { - sources = [ "strings/escape_fuzzer.cc" ] - deps = [ "//base" ] -} - -fuzzer_test("file_path_fuzzer") { - sources = [ "files/file_path_fuzzer.cc" ] - deps = [ "//base" ] -} - -fuzzer_test("string_number_conversions_fuzzer") { - sources = [ "strings/string_number_conversions_fuzzer.cc" ] - deps = [ "//base" ] -} - -fuzzer_test("string_pattern_fuzzer") { - sources = [ "strings/pattern_fuzzer.cc" ] - deps = [ "//base" ] -} - -fuzzer_test("string_tokenizer_fuzzer") { - sources = [ "strings/string_tokenizer_fuzzer.cc" ] - deps = [ "//base" ] -} - -fuzzer_test("substring_set_matcher_fuzzer") { - sources = [ "substring_set_matcher/substring_set_matcher_fuzzer.cc" ] - deps = [ "//base" ] -} - -fuzzer_test("sys_string_conversions_fuzzer") { - sources = [ "strings/sys_string_conversions_fuzzer.cc" ] - deps = [ "//base" ] -} - -fuzzer_test("time_delta_from_string_fuzzer") { - sources = [ "time/time_delta_from_string_fuzzer.cc" ] - deps = [ "//base" ] -} - -fuzzer_test("time_fuzzer") { - sources = [ "time/time_fuzzer.cc" ] - deps = [ - "//base", - "//base/test:test_support", - ] -} - -fuzzer_test("utf_string_conversions_fuzzer") { - sources = [ "strings/utf_string_conversions_fuzzer.cc" ] - deps = [ "//base" ] -} - -fuzzer_test("pickle_fuzzer") { - sources = [ "pickle_fuzzer.cc" ] - deps = [ - "//base", - "//base/test:test_support", - ] -} - -# TODO(1151236): Temporarily move test_support from -# //base/allocator/partition_allocator/BUILD.gn to //base/BUILD.gn. After -# fixing partition_allocator tests issue, we will move test_support to -# //base/allocator/partition_allocator/BUILD.gn again. -source_set("partition_alloc_test_support") { - testonly = true - - sources = [ - "allocator/partition_allocator/extended_api.cc", - "allocator/partition_allocator/extended_api.h", - "allocator/partition_allocator/partition_alloc_base/threading/platform_thread_for_testing.h", - "allocator/partition_allocator/partition_alloc_for_testing.h", - ] - if (is_posix) { - sources += [ "allocator/partition_allocator/partition_alloc_base/threading/platform_thread_posix_for_testing.cc" ] - } - if (is_fuchsia) { - sources += [ - "allocator/partition_allocator/partition_alloc_base/threading/platform_thread_fuchsia_for_testing.cc", - "allocator/partition_allocator/partition_alloc_base/threading/platform_thread_posix_for_testing.cc", - ] - } - if (is_win) { - sources += [ "allocator/partition_allocator/partition_alloc_base/threading/platform_thread_win_for_testing.cc" ] - } - if (is_mac || is_ios) { - sources += [ "allocator/partition_allocator/partition_alloc_base/threading/platform_thread_mac_for_testing.mm" ] - } - if (is_linux || is_chromeos) { - sources += [ "allocator/partition_allocator/partition_alloc_base/threading/platform_thread_linux_for_testing.cc" ] - } - if (is_android) { - sources += [ "allocator/partition_allocator/partition_alloc_base/threading/platform_thread_android_for_testing.cc" ] - } - public_deps = [ - "allocator/partition_allocator:buildflags", - "//build:branding_buildflags", - "//build/config/compiler:compiler_buildflags", - ] - public_configs = [] - if (is_fuchsia) { - public_deps += [ - "//third_party/fuchsia-sdk/sdk/pkg/fit", - "//third_party/fuchsia-sdk/sdk/pkg/sync", - "//third_party/fuchsia-sdk/sdk/pkg/zx", - ] - - # Needed for users of spinning_mutex.h, which for performance reasons, - # contains inlined calls to `libsync` inside the header file. - # It appends an entry to the "libs" section of the dependent target. - public_configs += [ ":fuchsia_sync_lib" ] - } - deps = [ ":base" ] - - if (!is_debug) { - configs -= [ "//build/config/compiler:default_optimization" ] - configs += [ "//build/config/compiler:optimize_speed" ] - } - - if (is_starboard) { - sources = [] - } -}
diff --git a/base/DEPS b/base/DEPS deleted file mode 100644 index 679f6cc..0000000 --- a/base/DEPS +++ /dev/null
@@ -1,41 +0,0 @@ -include_rules = [ - "+third_party/ashmem", - "+third_party/apple_apsl", - "+third_party/boringssl/src/include", - "+third_party/ced", - "+third_party/libevent", - "+third_party/libunwindstack/src/libunwindstack/include", - "+third_party/lss", - "+third_party/modp_b64", - "+third_party/perfetto/include", - "+third_party/perfetto/protos/perfetto", - # Conversions between base and Rust types (e.g. base::span <-> rust::Slice) - # require the cxx.h header from cxx. This is only used if Rust is enabled - # in the gn build; see //base/BUILD.gn's conditional dependency on - # //build/rust:cxx_cppdeps. - "+third_party/rust/cxx", - "+third_party/test_fonts", - # JSON Deserialization. - "+third_party/rust/serde_json_lenient/v0_1/wrapper", - - # These are implicitly brought in from the root, and we don't want them. - "-ipc", - "-url", - - # ICU dependendencies must be separate from the rest of base. - "-i18n", - - # //base/util can use //base but not vice versa. - "-util", -] - -specific_include_rules = { - # Special case - "process/current_process(|_test)\.h": [ - "+third_party/perfetto/protos/perfetto/trace/track_event/chrome_process_descriptor.pbzero.h", - ], - # To evaluate the performance effects of using absl's flat_hash_map. - "supports_user_data\.h": [ - "+third_party/abseil-cpp/absl/container/flat_hash_map.h", - ] -}
diff --git a/base/DIR_METADATA b/base/DIR_METADATA deleted file mode 100644 index 2aaf8e2..0000000 --- a/base/DIR_METADATA +++ /dev/null
@@ -1,3 +0,0 @@ -monorail { - component: "Internals>Core" -}
diff --git a/base/METADATA b/base/METADATA deleted file mode 100644 index 3d9342e..0000000 --- a/base/METADATA +++ /dev/null
@@ -1,24 +0,0 @@ -name: "base" -description: - "Subtree at base." - -third_party { - identifier { - type: "ChromiumVersion" - value: "114.0.5735.358" # from https://chromereleases.googleblog.com/2024/03/long-term-support-channel-update-for_26.html - } - identifier { - type: "Git" - value: "https://chromium.googlesource.com/chromium/src.git" - version: "1759c6ae9316996b9f150c0ce9d0ca78a3d15c02" - } - identifier { - type: "UpstreamSubdir" - value: "base" - } - last_upgrade_date { - year: 2023 - month: 9 - day: 12 - } -}
diff --git a/base/OWNERS b/base/OWNERS deleted file mode 100644 index a1b94bc..0000000 --- a/base/OWNERS +++ /dev/null
@@ -1,51 +0,0 @@ -# See //base/README.md to find qualification for being an owner. - -set noparent -# NOTE: keep this in sync with lsc-owners-override@chromium.org owners -# by emailing lsc-policy@chromium.org when this list changes. -altimin@chromium.org -danakj@chromium.org -dcheng@chromium.org -fdoray@chromium.org -gab@chromium.org -kylechar@chromium.org -mark@chromium.org -thakis@chromium.org -thestig@chromium.org -wez@chromium.org -# NOTE: keep this in sync with lsc-owners-override@chromium.org owners -# by emailing lsc-policy@chromium.org when this list changes. - -# per-file rules: -# These are for the common case of adding or renaming files. If you're doing -# structural changes, please get a review from a reviewer in this file. -per-file BUILD.gn=* - -# For Android-specific changes: -per-file ..._android*=file://base/android/OWNERS - -# For Fuchsia-specific changes: -per-file ..._fuchsia*=file://build/fuchsia/OWNERS - -# For Windows-specific changes: -per-file ..._win*=file://base/win/OWNERS - -per-file callback_list*=pkasting@chromium.org -per-file feature_list*=asvitkine@chromium.org -per-file feature_list*=isherman@chromium.org - -# Logging-related changes: -per-file check*=olivierli@chromium.org -per-file check*=pbos@chromium.org -per-file dcheck*=olivierli@chromium.org -per-file dcheck*=pbos@chromium.org -per-file logging*=olivierli@chromium.org -per-file logging*=pbos@chromium.org -per-file notreached.h=olivierli@chromium.org -per-file notreached.h=pbos@chromium.org - -# Restricted since rand_util.h also backs the cryptographically secure RNG. -per-file rand_util*=set noparent -per-file rand_util*=file://ipc/SECURITY_OWNERS - -per-file safe_numerics_unittest.cc=file://base/numerics/OWNERS
diff --git a/base/PRESUBMIT.py b/base/PRESUBMIT.py deleted file mode 100644 index 13c1706..0000000 --- a/base/PRESUBMIT.py +++ /dev/null
@@ -1,163 +0,0 @@ -# Copyright 2012 The Chromium Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Chromium presubmit script for src/base. - -See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts -for more details on the presubmit API built into depot_tools. -""" - - -USE_PYTHON3 = True - - -def CheckChangeLintsClean(input_api, output_api): - """Makes sure that the code is cpplint clean.""" - # lint_filters=[] stops the OFF_BY_DEFAULT_LINT_FILTERS from being disabled, - # finding many more issues. verbose_level=1 finds a small number of additional - # issues. - # The only valid extensions for cpplint are .cc, .h, .cpp, .cu, and .ch. - # Only process those extensions which are used in Chromium, in directories - # that currently lint clean. - CLEAN_CPP_FILES_ONLY = (r'base/win/.*\.(cc|h)$', ) - source_file_filter = lambda x: input_api.FilterSourceFile( - x, - files_to_check=CLEAN_CPP_FILES_ONLY, - files_to_skip=input_api.DEFAULT_FILES_TO_SKIP) - return input_api.canned_checks.CheckChangeLintsClean( - input_api, output_api, source_file_filter=source_file_filter, - lint_filters=[], verbose_level=1) - - -def _CheckNoInterfacesInBase(input_api, output_api): - """Checks to make sure no files in libbase.a have |@interface|.""" - pattern = input_api.re.compile(r'^\s*@interface', input_api.re.MULTILINE) - files = [] - for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile): - if (f.LocalPath().startswith('base/') and - not "/ios/" in f.LocalPath() and - not "/test/" in f.LocalPath() and - not f.LocalPath().endswith('.java') and - not f.LocalPath().endswith('_unittest.mm') and - not f.LocalPath().endswith('mac/sdk_forward_declarations.h')): - contents = input_api.ReadFile(f) - if pattern.search(contents): - files.append(f) - - if len(files): - return [ output_api.PresubmitError( - 'Objective-C interfaces or categories are forbidden in libbase. ' + - 'See http://groups.google.com/a/chromium.org/group/chromium-dev/' + - 'browse_thread/thread/efb28c10435987fd', - files) ] - return [] - - -def _FindLocations(input_api, search_regexes, files_to_check, files_to_skip): - """Returns locations matching one of the search_regexes.""" - def FilterFile(affected_file): - return input_api.FilterSourceFile( - affected_file, - files_to_check=files_to_check, - files_to_skip=files_to_skip) - - no_presubmit = r"// no-presubmit-check" - locations = [] - for f in input_api.AffectedSourceFiles(FilterFile): - for line_num, line in f.ChangedContents(): - for search_regex in search_regexes: - if (input_api.re.search(search_regex, line) and - not input_api.re.search(no_presubmit, line)): - locations.append(" %s:%d" % (f.LocalPath(), line_num)) - break - return locations - - -def _CheckNoTraceEventInclude(input_api, output_api): - """Verify that //base includes base_tracing.h instead of trace event headers. - - Checks that files outside trace event implementation include the - base_tracing.h header instead of specific trace event implementation headers - to maintain compatibility with the gn flag "enable_base_tracing = false". - """ - discouraged_includes = [ - r'^#include "base/trace_event/(?!base_tracing\.h|base_tracing_forward\.h)', - r'^#include "third_party/perfetto/include/', - ] - - files_to_check = [ - r".*\.(h|cc|mm)$", - ] - files_to_skip = [ - r".*/test/.*", - r".*/trace_event/.*", - r".*/tracing/.*", - ] - - locations = _FindLocations(input_api, discouraged_includes, files_to_check, - files_to_skip) - if locations: - return [ output_api.PresubmitError( - 'Base code should include "base/trace_event/base_tracing.h" instead\n' + - 'of trace_event implementation headers. If you need to include an\n' + - 'implementation header, verify that "gn check" and base_unittests\n' + - 'still pass with gn arg "enable_base_tracing = false" and add\n' + - '"// no-presubmit-check" after the include. \n' + - '\n'.join(locations)) ] - return [] - - -def _WarnPbzeroIncludes(input_api, output_api): - """Warn to check enable_base_tracing=false when including a pbzero header. - - Emits a warning when including a perfetto pbzero header, encouraging the - user to verify that //base still builds with enable_base_tracing=false. - """ - warn_includes = [ - r'^#include "third_party/perfetto/protos/', - r'^#include "base/tracing/protos/', - ] - - files_to_check = [ - r".*\.(h|cc|mm)$", - ] - files_to_skip = [ - r".*/test/.*", - r".*/trace_event/.*", - r".*/tracing/.*", - ] - - locations = _FindLocations(input_api, warn_includes, files_to_check, - files_to_skip) - if locations: - return [ output_api.PresubmitPromptWarning( - 'Please verify that "gn check" and base_unittests still pass with\n' + - 'gn arg "enable_base_tracing = false" when adding typed trace\n' + - 'events to //base. You can use "#if BUILDFLAG(ENABLE_BASE_TRACING)"\n' + - 'to exclude pbzero headers and anything not supported by\n' + - '//base/trace_event/trace_event_stub.h.\n' + - '\n'.join(locations)) ] - return [] - - -def _CommonChecks(input_api, output_api): - """Checks common to both upload and commit.""" - results = [] - results.extend(_CheckNoInterfacesInBase(input_api, output_api)) - results.extend(_CheckNoTraceEventInclude(input_api, output_api)) - results.extend(_WarnPbzeroIncludes(input_api, output_api)) - results.extend(CheckChangeLintsClean(input_api, output_api)) - return results - - -def CheckChangeOnUpload(input_api, output_api): - results = [] - results.extend(_CommonChecks(input_api, output_api)) - return results - - -def CheckChangeOnCommit(input_api, output_api): - results = [] - results.extend(_CommonChecks(input_api, output_api)) - return results
diff --git a/base/README.md b/base/README.md deleted file mode 100644 index ef5a259..0000000 --- a/base/README.md +++ /dev/null
@@ -1,87 +0,0 @@ -# What is this -Contains a written down set of principles and other information on //base. -Please add to it! - -## About //base: - -Chromium is a very mature project. Most things that are generally useful are -already here and things not here aren't generally useful. - -The bar for adding stuff to base is that it must have demonstrated wide -applicability. Prefer to add things closer to where they're used (i.e. "not -base"), and pull into base only when needed. In a project our size, -sometimes even duplication is OK and inevitable. - -Adding a new logging macro `DPVELOG_NE` is not more clear than just -writing the stuff you want to log in a regular logging statement, even -if it makes your calling code longer. Just add it to your own code. - -If the code in question does not need to be used inside base, but will have -multiple consumers across the codebase, consider placing it in a new directory -under components/ instead. - -base is written for the Chromium project and is not intended to be used -outside it. Using base outside of src.git is explicitly not supported, -and base makes no guarantees about API (or even ABI) stability (like all -other code in Chromium). New code that depends on base/ must be in -src.git. Code that's not in src.git but pulled in through DEPS (for -example, v8) cannot use base. - -## Qualifications for being in //base OWNERS - * interest and ability to learn low level/high detail/complex c++ stuff - * inclination to always ask why and understand everything (including external - interactions like win32) rather than just hoping the author did it right - * mentorship/experience - * demonstrated good judgement (esp with regards to public APIs) over a length - of time - -Owners are added when a contributor has shown the above qualifications and -when they express interest. There isn't an upper bound on the number of OWNERS. - -## Design and naming - * Be sure to use the base namespace. - * STL-like constructs should adhere as closely to STL as possible. Functions - and behaviors not present in STL should only be added when they are related - to the specific data structure implemented by the container. - * For STL-like constructs our policy is that they should use STL-like naming - even when it may conflict with the style guide. So functions and class names - should be lower case with underscores. Non-STL-like classes and functions - should use Google naming. - -## Performance testing - -Since the primitives provided by //base are used very widely, it is important to -ensure they scale to the necessary workloads and perform well under all -supported platforms. The `base_perftests` target is a suite of -synthetic microbenchmarks that measure performance in various scenarios: - - * BasicPostTaskPerfTest: Exercises MessageLoopTaskRunner's multi-threaded - queue in isolation. - * ConditionVariablePerfTest: Measures thread switching cost of condition - variables. - * IntegratedPostTaskPerfTest: Exercises the full MessageLoop/RunLoop - machinery. - * JSONPerfTest: Tests JSONWriter and JSONReader performance. - * MessageLoopPerfTest: Measures the speed of task posting in various - configurations. - * ObserverListPerfTest: Exercises adding, removing and signalling observers. - * PartitionLockPerfTest: Tests the implementation of Lock used in - PartitionAlloc - * PthreadEventPerfTest: Establishes the baseline thread switching cost using - pthreads. - * RandUtilPerfTest: Measures the time it takes to generate random numbers. - * ScheduleWorkTest: Measures the overhead of MessagePump::ScheduleWork. - * SequenceManagerPerfTest: Benchmarks SequenceManager scheduling with various - underlying task runners. - * TaskObserverPerfTest: Measures the incremental cost of adding task - observers. - * TaskPerfTest: Checks the cost of posting tasks between threads. - * ThreadLocalStoragePerfTest: Exercises different mechanisms for accessing - data associated with the current thread (C++ `thread_local`, the - implementation in //base, the POSIX/WinAPI directly) - * WaitableEvent{Thread,}PerfTest: Measures waitable events in single and - multithreaded scenarios. - -Regressions in these benchmarks can generally by caused by 1) operating system -changes, 2) compiler version or flag changes or 3) changes in //base code -itself.
diff --git a/base/SECURITY_OWNERS b/base/SECURITY_OWNERS deleted file mode 100644 index 0747cfd..0000000 --- a/base/SECURITY_OWNERS +++ /dev/null
@@ -1,13 +0,0 @@ -# Changes to code that runs at high privilege and which has a high risk of -# memory corruption, such as parsers for complex inputs, require a security -# review to avoid introducing sandbox escapes. -# -# Although this file is in base/, it may apply to more than just base, OWNERS -# files outside of base may also include this file. -# -# Security team: If you are uncomfortable reviewing a particular bit of code -# yourself, don't hesitate to seek help from another security team member! -# Nobody knows everything, and the only way to learn is from experience. -dcheng@chromium.org -rsesek@chromium.org -tsepez@chromium.org
diff --git a/base/allocator/BUILD.gn b/base/allocator/BUILD.gn deleted file mode 100644 index d0ab023..0000000 --- a/base/allocator/BUILD.gn +++ /dev/null
@@ -1,78 +0,0 @@ -# Copyright 2013 The Chromium Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//base/allocator/allocator.gni") -import("//base/allocator/partition_allocator/partition_alloc.gni") -import("//build/buildflag_header.gni") -import("//build/config/compiler/compiler.gni") -import("//build/config/dcheck_always_on.gni") - -buildflag_header("buildflags") { - header = "buildflags.h" - - assert(use_allocator_shim || !use_partition_alloc_as_malloc, - "PartitionAlloc-Everywhere requires the allocator shim") - - flags = [ - "USE_ALLOCATOR_SHIM=$use_allocator_shim", - - "USE_PARTITION_ALLOC_AS_GWP_ASAN_STORE=$enable_backup_ref_ptr_support", - - "USE_ALLOCATION_EVENT_DISPATCHER=$use_allocation_event_dispatcher", - ] -} - -if (is_apple) { - source_set("early_zone_registration_mac") { - sources = [ - "early_zone_registration_mac.cc", - "early_zone_registration_mac.h", - ] - - deps = [ - ":buildflags", - "//base/allocator/partition_allocator:buildflags", - ] - } -} - -# Used to shim malloc symbols on Android. see //base/allocator/README.md. -config("wrap_malloc_symbols") { - ldflags = [ - "-Wl,-wrap,calloc", - "-Wl,-wrap,free", - "-Wl,-wrap,malloc", - "-Wl,-wrap,memalign", - "-Wl,-wrap,posix_memalign", - "-Wl,-wrap,pvalloc", - "-Wl,-wrap,realloc", - "-Wl,-wrap,valloc", - - # Not allocating memory, but part of the API - "-Wl,-wrap,malloc_usable_size", - - # <stdlib.h> functions - "-Wl,-wrap,realpath", - - # <string.h> functions - "-Wl,-wrap,strdup", - "-Wl,-wrap,strndup", - - # <unistd.h> functions - "-Wl,-wrap,getcwd", - - # <stdio.h> functions - "-Wl,-wrap,asprintf", - "-Wl,-wrap,vasprintf", - ] -} - -config("mac_no_default_new_delete_symbols") { - if (!is_component_build) { - # This is already set when we compile libc++, see - # buildtools/third_party/libc++/BUILD.gn. But it needs to be set here as well, - # since the shim defines the symbols, to prevent them being exported. - cflags = [ "-fvisibility-global-new-delete-hidden" ] - } -}
diff --git a/base/allocator/DIR_METADATA b/base/allocator/DIR_METADATA deleted file mode 100644 index 14b5edb..0000000 --- a/base/allocator/DIR_METADATA +++ /dev/null
@@ -1,3 +0,0 @@ -monorail { - component: "Internals" -}
diff --git a/base/allocator/OWNERS b/base/allocator/OWNERS deleted file mode 100644 index e27d563..0000000 --- a/base/allocator/OWNERS +++ /dev/null
@@ -1,7 +0,0 @@ -lizeb@chromium.org -primiano@chromium.org -wfh@chromium.org - -per-file allocator.gni=file://base/allocator/partition_allocator/OWNERS -per-file partition_alloc*=file://base/allocator/partition_allocator/OWNERS -per-file BUILD.gn=file://base/allocator/partition_allocator/OWNERS
diff --git a/base/allocator/README.md b/base/allocator/README.md deleted file mode 100644 index d1562ea..0000000 --- a/base/allocator/README.md +++ /dev/null
@@ -1,156 +0,0 @@ -This document describes how malloc / new calls are routed in the various Chrome -platforms. - -Bear in mind that the chromium codebase does not always just use `malloc()`. -Some examples: - - Large parts of the renderer (Blink) use two home-brewed allocators, - PartitionAlloc and BlinkGC (Oilpan). - - Some subsystems, such as the V8 JavaScript engine, handle memory management - autonomously. - - Various parts of the codebase use abstractions such as `SharedMemory` or - `DiscardableMemory` which, similarly to the above, have their own page-level - memory management. - -Background ----------- -The `allocator` target defines at compile-time the platform-specific choice of -the allocator and extra-hooks which services calls to malloc/new. The relevant -build-time flags involved are `use_allocator_shim` and -`use_partition_alloc_as_malloc`. - -By default, these are true on all platforms except iOS (not yet supported) and -NaCl (no plan to support). -Furthermore, when building with a sanitizer (e.g. `asan`, `msan`, ...) both the -allocator and the shim layer are disabled. - - -Layering and build deps ------------------------ -The `allocator` target provides the linker flags required for the Windows shim -layer. The `base` target is (almost) the only one depending on `allocator`. No -other targets should depend on it, with the exception of the very few -executables / dynamic libraries that don't depend, either directly or -indirectly, on `base` within the scope of a linker unit. - -More importantly, **no other place outside of `/base` should depend on the -specific allocator**. -If such a functional dependency is required that should be achieved using -abstractions in `base` (see `/base/allocator/allocator_extension.h` and -`/base/memory/`) - -**Why `base` depends on `allocator`?** -Because it needs to provide services that depend on the actual allocator -implementation. In the past `base` used to pretend to be allocator-agnostic -and get the dependencies injected by other layers. This ended up being an -inconsistent mess. -See the [allocator cleanup doc][url-allocator-cleanup] for more context. - -Linker unit targets (executables and shared libraries) that depend in some way -on `base` (most of the targets in the codebase) automatically get the correct -set of linker flags to pull in the Windows shim-layer (if needed). - - -Source code ------------ -This directory contains just the allocator (i.e. shim) layer that switches -between the different underlying memory allocation implementations. - - -Unified allocator shim ----------------------- -On most platforms, Chrome overrides the malloc / operator new symbols (and -corresponding free / delete and other variants). This is to enforce security -checks and lately to enable the -[memory-infra heap profiler][url-memory-infra-heap-profiler]. -Historically each platform had its special logic for defining the allocator -symbols in different places of the codebase. The unified allocator shim is -a project aimed to unify the symbol definition and allocator routing logic in -a central place. - - - Full documentation: [Allocator shim design doc][url-allocator-shim]. - - Current state: Available and enabled by default on Android, CrOS, Linux, - Mac OS and Windows. - - Tracking bug: [crbug.com/550886](https://crbug.com/550886). - - Build-time flag: `use_allocator_shim`. - -**Overview of the unified allocator shim** -The allocator shim consists of three stages: -``` -+-------------------------+ +-----------------------+ +----------------+ -| malloc & friends | -> | shim layer | -> | Routing to | -| symbols definition | | implementation | | allocator | -+-------------------------+ +-----------------------+ +----------------+ -| - libc symbols (malloc, | | - Security checks | | - glibc | -| calloc, free, ...) | | - Chain of dispatchers| | - Android | -| - C++ symbols (operator | | that can intercept | | bionic | -| new, delete, ...) | | and override | | - WinHeap | -| - glibc weak symbols | | allocations | | - Partition | -| (__libc_malloc, ...) | +-----------------------+ | Alloc | -+-------------------------+ +----------------+ -``` - -**1. malloc symbols definition** -This stage takes care of overriding the symbols `malloc`, `free`, -`operator new`, `operator delete` and friends and routing those calls inside the -allocator shim (next point). -This is taken care of by the headers in `allocator_shim_override_*`. - -*On Windows*: Windows' UCRT (Universal C Runtime) exports weak symbols, that we -can override in `allocator_shim_override_ucr_symbols_win.h`. - -*On Linux/CrOS*: the allocator symbols are defined as exported global symbols -in `allocator_shim_override_libc_symbols.h` (for `malloc`, `free` and friends) -and in `allocator_shim_override_cpp_symbols.h` (for `operator new`, -`operator delete` and friends). -This enables proper interposition of malloc symbols referenced by the main -executable and any third party libraries. Symbol resolution on Linux is a breadth first search that starts from the root link unit, that is the executable -(see EXECUTABLE AND LINKABLE FORMAT (ELF) - Portable Formats Specification). -The Linux/CrOS shim was introduced by -[crrev.com/1675143004](https://crrev.com/1675143004). - -*On Android*: load-time symbol interposition (unlike the Linux/CrOS case) is not -possible. This is because Android processes are `fork()`-ed from the Android -zygote, which pre-loads libc.so and only later native code gets loaded via -`dlopen()` (symbols from `dlopen()`-ed libraries get a different resolution -scope). -In this case, the approach instead of wrapping symbol resolution at link time -(i.e. during the build), via the `--Wl,-wrap,malloc` linker flag. -The use of this wrapping flag causes: - - All references to allocator symbols in the Chrome codebase to be rewritten as - references to `__wrap_malloc` and friends. The `__wrap_malloc` symbols are - defined in the `allocator_shim_override_linker_wrapped_symbols.h` and - route allocator calls inside the shim layer. - - The reference to the original `malloc` symbols (which typically is defined by - the system's libc.so) are accessible via the special `__real_malloc` and - friends symbols (which will be relocated, at load time, against `malloc`). - -In summary, this approach is transparent to the dynamic loader, which still sees -undefined symbol references to malloc symbols. -These symbols will be resolved against libc.so as usual. -More details in [crrev.com/1719433002](https://crrev.com/1719433002). - -**2. Shim layer implementation** -This stage contains the actual shim implementation. This consists of: -- A singly linked list of dispatchers (structs with function pointers to `malloc`-like functions). Dispatchers can be dynamically inserted at runtime -(using the `InsertAllocatorDispatch` API). They can intercept and override -allocator calls. -- The security checks (suicide on malloc-failure via `std::new_handler`, etc). -This happens inside `allocator_shim.cc` - -**3. Final allocator routing** -The final element of the aforementioned dispatcher chain is statically defined -at build time and ultimately routes the allocator calls to the actual allocator -(as described in the *Background* section above). This is taken care of by the -headers in `allocator_shim_default_dispatch_to_*` files. - - -Related links -------------- -- [Unified allocator shim doc - Feb 2016][url-allocator-shim] -- [Allocator cleanup doc - Jan 2016][url-allocator-cleanup] -- [Proposal to use PartitionAlloc as default allocator](https://crbug.com/339604) -- [Memory-Infra: Tools to profile memory usage in Chrome](/docs/memory-infra/README.md) - -[url-allocator-cleanup]: https://docs.google.com/document/d/1V77Kgp_4tfaaWPEZVxNevoD02wXiatnAv7Ssgr0hmjg/edit?usp=sharing -[url-memory-infra-heap-profiler]: /docs/memory-infra/heap_profiler.md -[url-allocator-shim]: https://docs.google.com/document/d/1yKlO1AO4XjpDad9rjcBOI15EKdAGsuGO_IeZy0g0kxo/edit?usp=sharing
diff --git a/base/allocator/allocator.gni b/base/allocator/allocator.gni deleted file mode 100644 index 9ea03a4..0000000 --- a/base/allocator/allocator.gni +++ /dev/null
@@ -1,51 +0,0 @@ -# Copyright 2019 The Chromium Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//base/allocator/partition_allocator/partition_alloc.gni") -import("//build_overrides/partition_alloc.gni") - -if (is_ios) { - import("//build/config/ios/ios_sdk.gni") -} - -declare_args() { - # Causes all the allocations to be routed via allocator_shim.cc. - use_allocator_shim = use_allocator_shim_default - - # Use the new allocation event dispatcher to distribute events to event observers. - # If set to false, PoissonAllocationSampler will hook into PartitionAllocator and - # AllocatorShims directly. - use_allocation_event_dispatcher = true -} - -assert( - !use_allocator_shim || is_linux || is_chromeos || is_android || is_win || - is_fuchsia || is_apple, - "use_allocator_shim works only on Android, iOS, Linux, macOS, Fuchsia, " + - "and Windows.") - -if (is_win && use_allocator_shim) { - # TODO(crbug.com/1245317): Add a comment indicating why the shim doesn't work. - assert(!is_component_build, - "The allocator shim doesn't work for the component build on Windows.") -} - -# Chromium-specific asserts. External embedders _may_ elect to use these -# features even without PA-E. -if (!use_partition_alloc_as_malloc) { - # In theory, BackupRefPtr will work just fine without - # PartitionAlloc-Everywhere, but its scope would be limited to partitions - # that are invoked explicitly (not via malloc). These are only Blink - # partition, where we currently don't even use raw_ptr<T>. - assert(!enable_backup_ref_ptr_support, - "Chromium does not use BRP without PA-E") - - # Pointer compression works only if all pointers are guaranteed to be - # allocated by PA (in one of its core pools, to be precise). In theory, - # this could be useful with partitions that are invoked explicitly. In - # practice, the pointers we have in mind for compression (scoped_refptr<>, - # unique_ptr<>) require PA-E. - assert(!enable_pointer_compression_support, - "Pointer compressions likely doesn't make sense without PA-E") -}
diff --git a/base/allocator/allocator_check.cc b/base/allocator/allocator_check.cc deleted file mode 100644 index 90aa928..0000000 --- a/base/allocator/allocator_check.cc +++ /dev/null
@@ -1,39 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/allocator_check.h" - -#include "base/allocator/buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "build/build_config.h" - -#if BUILDFLAG(IS_WIN) -#include "base/allocator/partition_allocator/shim/winheap_stubs_win.h" -#endif - -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) -#include <malloc.h> -#endif - -#if BUILDFLAG(IS_APPLE) -#include "base/allocator/partition_allocator/shim/allocator_interception_mac.h" -#endif - -namespace base::allocator { - -bool IsAllocatorInitialized() { -#if BUILDFLAG(IS_WIN) && BUILDFLAG(USE_ALLOCATOR_SHIM) - // Set by allocator_shim_override_ucrt_symbols_win.h when the - // shimmed _set_new_mode() is called. - return allocator_shim::g_is_win_shim_layer_initialized; -#elif BUILDFLAG(IS_APPLE) && !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) && \ - !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - // From allocator_interception_mac.mm. - return allocator_shim::g_replaced_default_zone; -#else - return true; -#endif -} - -} // namespace base::allocator
diff --git a/base/allocator/allocator_check.h b/base/allocator/allocator_check.h deleted file mode 100644 index 58add8b..0000000 --- a/base/allocator/allocator_check.h +++ /dev/null
@@ -1,18 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_ALLOCATOR_CHECK_H_ -#define BASE_ALLOCATOR_ALLOCATOR_CHECK_H_ - -#include "base/base_export.h" - -namespace base { -namespace allocator { - -BASE_EXPORT bool IsAllocatorInitialized(); - -} // namespace allocator -} // namespace base - -#endif // BASE_ALLOCATOR_ALLOCATOR_CHECK_H_
diff --git a/base/allocator/allocator_extension.cc b/base/allocator/allocator_extension.cc deleted file mode 100644 index 88206df..0000000 --- a/base/allocator/allocator_extension.cc +++ /dev/null
@@ -1,15 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/allocator_extension.h" -#include "base/allocator/buildflags.h" -#include "base/check.h" - -namespace base { -namespace allocator { - -void ReleaseFreeMemory() {} - -} // namespace allocator -} // namespace base
diff --git a/base/allocator/allocator_extension.h b/base/allocator/allocator_extension.h deleted file mode 100644 index fbc9d1d..0000000 --- a/base/allocator/allocator_extension.h +++ /dev/null
@@ -1,23 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_ALLOCATOR_EXTENSION_H_ -#define BASE_ALLOCATOR_ALLOCATOR_EXTENSION_H_ - -#include <stddef.h> // for size_t - -#include "base/base_export.h" -#include "build/build_config.h" - -namespace base { -namespace allocator { - -// Request that the allocator release any free memory it knows about to the -// system. -BASE_EXPORT void ReleaseFreeMemory(); - -} // namespace allocator -} // namespace base - -#endif // BASE_ALLOCATOR_ALLOCATOR_EXTENSION_H_
diff --git a/base/allocator/dispatcher/configuration.h b/base/allocator/dispatcher/configuration.h deleted file mode 100644 index 34c3791..0000000 --- a/base/allocator/dispatcher/configuration.h +++ /dev/null
@@ -1,24 +0,0 @@ -// 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. - -#ifndef BASE_ALLOCATOR_DISPATCHER_CONFIGURATION_H_ -#define BASE_ALLOCATOR_DISPATCHER_CONFIGURATION_H_ - -#include <cstddef> - -namespace base::allocator::dispatcher::configuration { - -// The maximum number of optional observers that may be present depending on -// command line parameters. -constexpr size_t kMaximumNumberOfOptionalObservers = 4; - -// The total number of observers including mandatory and optional observers. -// Primarily the number of observers affects the performance at allocation time. -// The current value of 4 doesn't have hard evidence. Keep in mind that -// also a single observer can severely impact performance. -constexpr size_t kMaximumNumberOfObservers = 4; - -} // namespace base::allocator::dispatcher::configuration - -#endif // BASE_ALLOCATOR_DISPATCHER_CONFIGURATION_H_ \ No newline at end of file
diff --git a/base/allocator/dispatcher/dispatcher.cc b/base/allocator/dispatcher/dispatcher.cc deleted file mode 100644 index aa0e0ae..0000000 --- a/base/allocator/dispatcher/dispatcher.cc +++ /dev/null
@@ -1,114 +0,0 @@ -// 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. - -#include "base/allocator/dispatcher/dispatcher.h" - -#include "base/allocator/buildflags.h" -#include "base/allocator/dispatcher/internal/dispatch_data.h" -#if !defined(COBALT_PENDING_CLEAN_UP) -#include "base/allocator/partition_allocator/partition_alloc.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/shim/allocator_shim.h" -#endif -#include "base/check.h" -#include "base/dcheck_is_on.h" -#include "base/no_destructor.h" - -#if DCHECK_IS_ON() -#include <atomic> -#endif - -namespace base::allocator::dispatcher { - -// The private implementation of Dispatcher. -struct Dispatcher::Impl { - void Initialize(const internal::DispatchData& dispatch_data) { -#if DCHECK_IS_ON() - DCHECK(!is_initialized_check_flag_.test_and_set()); -#endif - - dispatch_data_ = dispatch_data; - ConnectToEmitters(dispatch_data_); - } - - void Reset() { -#if DCHECK_IS_ON() - DCHECK([&]() { - auto const was_set = is_initialized_check_flag_.test_and_set(); - is_initialized_check_flag_.clear(); - return was_set; - }()); -#endif - - DisconnectFromEmitters(dispatch_data_); - dispatch_data_ = {}; - } - - private: - // Connect the hooks to the memory subsystem. In some cases, most notably when - // we have no observers at all, the hooks will be invalid and must NOT be - // connected. This way we prevent notifications although no observers are - // present. - static void ConnectToEmitters(const internal::DispatchData& dispatch_data) { -#if BUILDFLAG(USE_ALLOCATOR_SHIM) - if (auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch()) { - allocator_shim::InsertAllocatorDispatch(allocator_dispatch); - } -#endif - -#if BUILDFLAG(USE_PARTITION_ALLOC) - { - auto* const allocation_hook = dispatch_data.GetAllocationObserverHook(); - auto* const free_hook = dispatch_data.GetFreeObserverHook(); - if (allocation_hook && free_hook) { - partition_alloc::PartitionAllocHooks::SetObserverHooks(allocation_hook, - free_hook); - } - } -#endif - } - - static void DisconnectFromEmitters(internal::DispatchData& dispatch_data) { -#if BUILDFLAG(USE_ALLOCATOR_SHIM) - if (auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch()) { - allocator_shim::RemoveAllocatorDispatchForTesting( - allocator_dispatch); // IN-TEST - } -#endif - -#if BUILDFLAG(USE_PARTITION_ALLOC) - partition_alloc::PartitionAllocHooks::SetObserverHooks(nullptr, nullptr); -#endif - } - - // Information on the hooks. - internal::DispatchData dispatch_data_; -#if DCHECK_IS_ON() - // Indicator if the dispatcher has been initialized before. -#if !defined(__cpp_lib_atomic_value_initialization) || \ - __cpp_lib_atomic_value_initialization < 201911L - std::atomic_flag is_initialized_check_flag_ = ATOMIC_FLAG_INIT; -#else - std::atomic_flag is_initialized_check_flag_; -#endif -#endif -}; - -Dispatcher::Dispatcher() : impl_(std::make_unique<Impl>()) {} - -Dispatcher::~Dispatcher() = default; - -Dispatcher& Dispatcher::GetInstance() { - static base::NoDestructor<Dispatcher> instance; - return *instance; -} - -void Dispatcher::Initialize(const internal::DispatchData& dispatch_data) { - impl_->Initialize(dispatch_data); -} - -void Dispatcher::ResetForTesting() { - impl_->Reset(); -} -} // namespace base::allocator::dispatcher
diff --git a/base/allocator/dispatcher/dispatcher.h b/base/allocator/dispatcher/dispatcher.h deleted file mode 100644 index b8fc677..0000000 --- a/base/allocator/dispatcher/dispatcher.h +++ /dev/null
@@ -1,75 +0,0 @@ -// 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. - -#ifndef BASE_ALLOCATOR_DISPATCHER_DISPATCHER_H_ -#define BASE_ALLOCATOR_DISPATCHER_DISPATCHER_H_ - -#include "base/allocator/dispatcher/internal/dispatcher_internal.h" -#include "base/base_export.h" - -#include <memory> - -namespace base::allocator::dispatcher { - -namespace internal { -struct DispatchData; -} - -// Dispatcher serves as the top level instance for managing the dispatch -// mechanism. The class instance manages connections to the various memory -// subsystems such as PartitionAlloc. To keep the public interface as lean as -// possible it uses a pimpl pattern. -class BASE_EXPORT Dispatcher { - public: - static Dispatcher& GetInstance(); - - Dispatcher(); - - // Initialize the dispatch mechanism with the given tuple of observers. The - // observers must be valid (it is only DCHECKed internally at initialization, - // but not verified further) - // If Initialize is called multiple times, the first one wins. All later - // invocations are silently ignored. Initialization is protected from - // concurrent invocations. In case of concurrent accesses, the first one to - // get the lock wins. - // The dispatcher invokes following functions on the observers: - // void OnAllocation(void* address, - // size_t size, - // AllocationSubsystem sub_system, - // const char* type_name); - // void OnFree(void* address); - // - // Note: The dispatcher mechanism does NOT bring systematic protection against - // recursive invocations. That is, observers which allocate memory on the - // heap, i.e. through dynamically allocated containers or by using the - // CHECK-macro, are responsible to break these recursions! - template <typename... ObserverTypes> - void Initialize(const std::tuple<ObserverTypes...>& observers) { - // Get the hooks for running these observers and pass them to further - // initialization - Initialize(internal::GetNotificationHooks(observers)); - } - - // The following functions provide an interface to setup and tear down the - // dispatcher when testing. This must NOT be used from production code since - // the hooks cannot be removed reliably under all circumstances. - template <typename ObserverType> - void InitializeForTesting(ObserverType* observer) { - Initialize(std::make_tuple(observer)); - } - - void ResetForTesting(); - - private: - // structure and pointer to the private implementation. - struct Impl; - std::unique_ptr<Impl> const impl_; - - ~Dispatcher(); - - void Initialize(const internal::DispatchData& dispatch_data); -}; -} // namespace base::allocator::dispatcher - -#endif // BASE_ALLOCATOR_DISPATCHER_DISPATCHER_H_
diff --git a/base/allocator/dispatcher/dispatcher_unittest.cc b/base/allocator/dispatcher/dispatcher_unittest.cc deleted file mode 100644 index 559493f..0000000 --- a/base/allocator/dispatcher/dispatcher_unittest.cc +++ /dev/null
@@ -1,154 +0,0 @@ -// 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. - -#include "testing/gtest/include/gtest/gtest.h" - -#include "base/allocator/buildflags.h" -#include "base/allocator/dispatcher/configuration.h" -#include "base/allocator/dispatcher/dispatcher.h" -#include "base/allocator/dispatcher/testing/dispatcher_test.h" -#include "base/allocator/dispatcher/testing/tools.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "build/build_config.h" - -#if BUILDFLAG(USE_PARTITION_ALLOC) -#include "base/allocator/partition_allocator/partition_alloc_for_testing.h" // nogncheck -#endif - -#if BUILDFLAG(USE_ALLOCATOR_SHIM) -#include "base/allocator/partition_allocator/shim/allocator_shim.h" -#endif - -#include <tuple> - -namespace base::allocator::dispatcher { -namespace { -using configuration::kMaximumNumberOfObservers; -using configuration::kMaximumNumberOfOptionalObservers; -#if BUILDFLAG(USE_PARTITION_ALLOC) -using partition_alloc::PartitionOptions; -using partition_alloc::ThreadSafePartitionRoot; -#endif -using testing::DispatcherTest; - -// A simple observer implementation. Since these tests plug in to Partition -// Allocator and Allocator Shim, implementing an observer with Google Mock -// results in endless recursion. -struct ObserverMock { - void OnAllocation(void* address, - size_t size, - AllocationSubsystem sub_system, - const char* type_name) { - ++on_allocation_calls_; - } - void OnFree(void* address) { ++on_free_calls_; } - - void Reset() { - on_allocation_calls_ = 0; - on_free_calls_ = 0; - } - - size_t GetNumberOnAllocationCalls() const { return on_allocation_calls_; } - size_t GetNumberOnFreeCalls() const { return on_free_calls_; } - - private: - size_t on_allocation_calls_ = 0; - size_t on_free_calls_ = 0; -}; - -struct DispatcherInitializerGuard { - template <typename... Observers> - explicit DispatcherInitializerGuard(std::tuple<Observers*...> observers) { - Dispatcher::GetInstance().Initialize(observers); - } - - ~DispatcherInitializerGuard() { Dispatcher::GetInstance().ResetForTesting(); } -}; - -struct BaseAllocatorDispatcherTest : public DispatcherTest {}; - -template <typename A> -void DoBasicTest(A& allocator) { - // All we want to verify is that the Dispatcher correctly hooks into the - // passed allocator. Therefore, we do not perform an exhaustive test but - // just check some basics. - std::array<ObserverMock, kMaximumNumberOfObservers> observers; - - { - DispatcherInitializerGuard const g( - testing::CreateTupleOfPointers(observers)); - - constexpr size_t size_to_allocate = 1024; - void* const ptr = allocator.Alloc(size_to_allocate); - allocator.Free(ptr); - } - - for (const auto& mock : observers) { - EXPECT_GE(mock.GetNumberOnAllocationCalls(), 1u); - EXPECT_GE(mock.GetNumberOnFreeCalls(), 1u); - } -} - -TEST_F(BaseAllocatorDispatcherTest, VerifyInitialization) { - std::array<ObserverMock, kMaximumNumberOfObservers> observers; - - DispatcherInitializerGuard g(testing::CreateTupleOfPointers(observers)); -} - -#if BUILDFLAG(USE_PARTITION_ALLOC) && !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) -// Don't enable this test when MEMORY_TOOL_REPLACES_ALLOCATOR is defined, -// because it makes PartitionAlloc take a different path that doesn't provide -// notifications to observer hooks. -struct PartitionAllocator { - void* Alloc(size_t size) { return alloc_.AllocWithFlags(0, size, nullptr); } - void Free(void* data) { alloc_.Free(data); } - ~PartitionAllocator() { - // Use |DisallowLeaks| to confirm that there is no memory allocated and - // not yet freed. - alloc_.ResetForTesting(::partition_alloc::internal::DisallowLeaks); - } - - private: - ThreadSafePartitionRoot alloc_{{ - PartitionOptions::AlignedAlloc::kDisallowed, - PartitionOptions::ThreadCache::kDisabled, - PartitionOptions::Quarantine::kDisallowed, - PartitionOptions::Cookie::kAllowed, - PartitionOptions::BackupRefPtr::kDisabled, - PartitionOptions::BackupRefPtrZapping::kDisabled, - PartitionOptions::UseConfigurablePool::kNo, - }}; -}; - -TEST_F(BaseAllocatorDispatcherTest, VerifyNotificationUsingPartitionAllocator) { - PartitionAllocator allocator; - DoBasicTest(allocator); -} -#endif - -#if BUILDFLAG(USE_ALLOCATOR_SHIM) -struct AllocatorShimAllocator { - void* Alloc(size_t size) { return allocator_shim::UncheckedAlloc(size); } - void Free(void* data) { allocator_shim::UncheckedFree(data); } -}; - -#if BUILDFLAG(IS_APPLE) && !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -// Disable the test when running on any of Apple's OSs without PartitionAlloc -// being the default allocator. In this case, all allocations are routed to -// MallocImpl, which then causes the test to terminate unexpectedly. -#define MAYBE_VerifyNotificationUsingAllocatorShim \ - DISABLED_VerifyNotificationUsingAllocatorShim -#else -#define MAYBE_VerifyNotificationUsingAllocatorShim \ - VerifyNotificationUsingAllocatorShim -#endif - -TEST_F(BaseAllocatorDispatcherTest, MAYBE_VerifyNotificationUsingAllocatorShim) { - AllocatorShimAllocator allocator; - DoBasicTest(allocator); -} -#endif - -} // namespace -} // namespace base::allocator::dispatcher
diff --git a/base/allocator/dispatcher/initializer.h b/base/allocator/dispatcher/initializer.h deleted file mode 100644 index 3179134..0000000 --- a/base/allocator/dispatcher/initializer.h +++ /dev/null
@@ -1,206 +0,0 @@ -// 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. - -#ifndef BASE_ALLOCATOR_DISPATCHER_INITIALIZER_H_ -#define BASE_ALLOCATOR_DISPATCHER_INITIALIZER_H_ - -#include "base/allocator/dispatcher/configuration.h" -#include "base/allocator/dispatcher/dispatcher.h" -#include "base/allocator/dispatcher/internal/tools.h" - -#include <tuple> -#include <utility> - -namespace base::allocator::dispatcher { -namespace internal { - -// Filter the passed observers and perform initialization of the passed -// dispatcher. -template <size_t CurrentIndex, - typename DispatcherType, - typename CheckObserverPredicate, - typename VerifiedObservers, - typename UnverifiedObservers, - size_t... IndicesToSelect> -inline void DoInitialize(DispatcherType& dispatcher, - CheckObserverPredicate check_observer, - const VerifiedObservers& verified_observers, - const UnverifiedObservers& unverified_observers, - std::index_sequence<IndicesToSelect...> indices) { - if constexpr (CurrentIndex < std::tuple_size<UnverifiedObservers>::value) { - // We still have some items left to handle. - if (check_observer(std::get<CurrentIndex>(unverified_observers))) { - // The current observer is valid. Hence, append the index of the current - // item to the set of indices and head on to the next item. - DoInitialize<CurrentIndex + 1>( - dispatcher, check_observer, verified_observers, unverified_observers, - std::index_sequence<IndicesToSelect..., CurrentIndex>{}); - } else { - // The current observer is not valid. Hence, head on to the next item with - // an unaltered list of indices. - DoInitialize<CurrentIndex + 1>(dispatcher, check_observer, - verified_observers, unverified_observers, - indices); - } - } else if constexpr (CurrentIndex == - std::tuple_size<UnverifiedObservers>::value) { - // So we have met the end of the tuple of observers to verify. - // Hence, we extract the additional valid observers, append to the tuple of - // already verified observers and hand over to the dispatcher. - auto observers = std::tuple_cat( - verified_observers, - std::make_tuple(std::get<IndicesToSelect>(unverified_observers)...)); - - // Do a final check that neither the maximum total number of observers nor - // the maximum number of optional observers is exceeded. - static_assert(std::tuple_size<decltype(observers)>::value <= - configuration::kMaximumNumberOfObservers); - static_assert(sizeof...(IndicesToSelect) <= - configuration::kMaximumNumberOfOptionalObservers); - - dispatcher.Initialize(std::move(observers)); - } -} - -} // namespace internal - -// The result of concatenating two tuple-types. -template <typename... tuples> -using TupleCat = decltype(std::tuple_cat(std::declval<tuples>()...)); - -// Initializer collects mandatory and optional observers and initializes the -// passed Dispatcher with only the enabled observers. -// -// In some situations, presence of observers depends on runtime. i.e. command -// line parameters or CPU features. With 3 optional observers we already have 8 -// different combinations. Initializer takes the job of dealing with all -// combinations from the user. It allows users to pass all observers (including -// nullptr for disabled optional observers) and initializes the Dispatcher with -// only the enabled observers. -// -// Since this process results in a combinatoric explosion, Initializer -// distinguishes between optional and mandatory observers. Mandatory observers -// are not included in the filtering process and must always be enabled (not -// nullptr). -// -// To allow the Initializer to track the number and exact type of observers, it -// is implemented as a templated class which holds information on the types in -// the std::tuples passed as template parameters. Therefore, whenever any type -// observer it set, the initializer changes its type to reflect this. -template <typename MandatoryObservers = std::tuple<>, - typename OptionalObservers = std::tuple<>> -struct BASE_EXPORT Initializer { - Initializer() = default; - Initializer(MandatoryObservers mandatory_observers, - OptionalObservers optional_observers) - : mandatory_observers_(std::move(mandatory_observers)), - optional_observers_(std::move(optional_observers)) {} - - // Set the mandatory observers. The number of observers that can be set is - // limited by configuration::maximum_number_of_observers. - template <typename... NewMandatoryObservers, - std::enable_if_t< - internal::LessEqual((sizeof...(NewMandatoryObservers) + - std::tuple_size<OptionalObservers>::value), - configuration::kMaximumNumberOfObservers), - bool> = true> - Initializer<std::tuple<NewMandatoryObservers*...>, OptionalObservers> - SetMandatoryObservers(NewMandatoryObservers*... mandatory_observers) const { - return {std::make_tuple(mandatory_observers...), GetOptionalObservers()}; - } - - // Add mandatory observers. The number of observers that can be added is - // limited by the current number of observers, see - // configuration::maximum_number_of_observers. - template <typename... AdditionalMandatoryObservers, - std::enable_if_t<internal::LessEqual( - std::tuple_size<MandatoryObservers>::value + - sizeof...(AdditionalMandatoryObservers) + - std::tuple_size<OptionalObservers>::value, - configuration::kMaximumNumberOfObservers), - bool> = true> - Initializer<TupleCat<MandatoryObservers, - std::tuple<AdditionalMandatoryObservers*...>>, - OptionalObservers> - AddMandatoryObservers( - AdditionalMandatoryObservers*... additional_mandatory_observers) const { - return {std::tuple_cat(GetMandatoryObservers(), - std::make_tuple(additional_mandatory_observers...)), - GetOptionalObservers()}; - } - - // Set the optional observers. The number of observers that can be set is - // limited by configuration::maximum_number_of_optional_observers as well as - // configuration::maximum_number_of_observers. - template < - typename... NewOptionalObservers, - std::enable_if_t< - internal::LessEqual( - sizeof...(NewOptionalObservers), - configuration::kMaximumNumberOfOptionalObservers) && - internal::LessEqual((sizeof...(NewOptionalObservers) + - std::tuple_size<MandatoryObservers>::value), - configuration::kMaximumNumberOfObservers), - bool> = true> - Initializer<MandatoryObservers, std::tuple<NewOptionalObservers*...>> - SetOptionalObservers(NewOptionalObservers*... optional_observers) const { - return {GetMandatoryObservers(), std::make_tuple(optional_observers...)}; - } - - // Add optional observers. The number of observers that can be added is - // limited by the current number of optional observers, - // configuration::maximum_number_of_optional_observers as well as - // configuration::maximum_number_of_observers. - template < - typename... AdditionalOptionalObservers, - std::enable_if_t< - internal::LessEqual( - std::tuple_size<OptionalObservers>::value + - sizeof...(AdditionalOptionalObservers), - configuration::kMaximumNumberOfOptionalObservers) && - internal::LessEqual((std::tuple_size<OptionalObservers>::value + - sizeof...(AdditionalOptionalObservers) + - std::tuple_size<MandatoryObservers>::value), - configuration::kMaximumNumberOfObservers), - bool> = true> - Initializer< - MandatoryObservers, - TupleCat<OptionalObservers, std::tuple<AdditionalOptionalObservers*...>>> - AddOptionalObservers( - AdditionalOptionalObservers*... additional_optional_observers) const { - return {GetMandatoryObservers(), - std::tuple_cat(GetOptionalObservers(), - std::make_tuple(additional_optional_observers...))}; - } - - // Perform the actual initialization on the passed dispatcher. - // The dispatcher is passed as a template only to provide better testability. - template <typename DispatcherType> - void DoInitialize(DispatcherType& dispatcher) const { - internal::DoInitialize<0>(dispatcher, internal::IsValidObserver{}, - GetMandatoryObservers(), GetOptionalObservers(), - {}); - } - - const MandatoryObservers& GetMandatoryObservers() const { - return mandatory_observers_; - } - - const OptionalObservers& GetOptionalObservers() const { - return optional_observers_; - } - - private: - MandatoryObservers mandatory_observers_; - OptionalObservers optional_observers_; -}; - -// Convenience function for creating an empty Initializer. -inline Initializer<> CreateInitializer() { - return {}; -} - -} // namespace base::allocator::dispatcher - -#endif // BASE_ALLOCATOR_DISPATCHER_INITIALIZER_H_ \ No newline at end of file
diff --git a/base/allocator/dispatcher/initializer_unittest.cc b/base/allocator/dispatcher/initializer_unittest.cc deleted file mode 100644 index f705830..0000000 --- a/base/allocator/dispatcher/initializer_unittest.cc +++ /dev/null
@@ -1,235 +0,0 @@ -// 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. - -#include "testing/gtest/include/gtest/gtest.h" - -#include "base/allocator/dispatcher/configuration.h" -#include "base/allocator/dispatcher/initializer.h" -#include "base/allocator/dispatcher/testing/observer_mock.h" -#include "base/allocator/dispatcher/testing/tools.h" - -#include <functional> -#include <map> -#include <tuple> - -namespace base::allocator::dispatcher { -namespace testing { - -// A mock Dispatcher for testing. Since Initializer and Dispatcher rely on -// templating, we can't employ GoogleMocks for mocking. The mock dispatcher -// records the number of invocations of Initialize for a given tuple of -// observers. -struct Dispatcher { - Dispatcher() = default; - - ~Dispatcher() { - for (const auto& reset_data : reseter_) { - reset_data.second(); - } - } - - template <typename... Observers> - void Initialize(const std::tuple<Observers*...>& observers) { - ++total_number_of_inits_; - ++(GetInitCounterForObservers(observers)); - } - - size_t GetTotalInitCounter() const { return total_number_of_inits_; } - - template <typename... Observers> - size_t& GetInitCounterForObservers( - const std::tuple<Observers*...>& observers) { - static std::map<std::tuple<Observers*...>, size_t> - observer_init_counter_map; - reseter_[&observer_init_counter_map] = []() { - observer_init_counter_map.clear(); - }; - return observer_init_counter_map[observers]; - } - - size_t total_number_of_inits_ = 0; - std::map<void*, std::function<void()>> reseter_; -}; -} // namespace testing - -using testing::ObserverMock; - -struct BaseAllocatorDispatcherInitializerTest : public ::testing::Test {}; - -TEST_F(BaseAllocatorDispatcherInitializerTest, VerifyEmptyInitializer) { - const auto initializer = CreateInitializer(); - - EXPECT_EQ(initializer.GetOptionalObservers(), std::make_tuple()); - EXPECT_EQ(initializer.GetMandatoryObservers(), std::make_tuple()); -} - -TEST_F(BaseAllocatorDispatcherInitializerTest, VerifySettingOptionalObservers) { - ObserverMock<int> optional_observer_1; - ObserverMock<float> optional_observer_2; - ObserverMock<size_t> optional_observer_3; - - auto initializer_1 = CreateInitializer().SetOptionalObservers( - &optional_observer_1, &optional_observer_2); - EXPECT_EQ(initializer_1.GetOptionalObservers(), - std::make_tuple(&optional_observer_1, &optional_observer_2)); - EXPECT_EQ(initializer_1.GetMandatoryObservers(), std::make_tuple()); - - auto initializer_2 = initializer_1.SetOptionalObservers(&optional_observer_3); - EXPECT_EQ(initializer_2.GetOptionalObservers(), - std::make_tuple(&optional_observer_3)); - EXPECT_EQ(initializer_2.GetMandatoryObservers(), std::make_tuple()); - - auto initializer_3 = initializer_2.SetOptionalObservers(); - EXPECT_EQ(initializer_3.GetOptionalObservers(), std::make_tuple()); - EXPECT_EQ(initializer_3.GetMandatoryObservers(), std::make_tuple()); -} - -TEST_F(BaseAllocatorDispatcherInitializerTest, VerifyAddingOptionalObservers) { - ObserverMock<int> optional_observer_1; - ObserverMock<float> optional_observer_2; - ObserverMock<size_t> optional_observer_3; - - auto initializer_1 = CreateInitializer().AddOptionalObservers( - &optional_observer_1, &optional_observer_2); - EXPECT_EQ(initializer_1.GetOptionalObservers(), - std::make_tuple(&optional_observer_1, &optional_observer_2)); - EXPECT_EQ(initializer_1.GetMandatoryObservers(), std::make_tuple()); - - auto initializer_2 = initializer_1.AddOptionalObservers(&optional_observer_3); - EXPECT_EQ(initializer_2.GetOptionalObservers(), - std::make_tuple(&optional_observer_1, &optional_observer_2, - &optional_observer_3)); - EXPECT_EQ(initializer_2.GetMandatoryObservers(), std::make_tuple()); - - auto initializer_3 = initializer_2.AddOptionalObservers(); - EXPECT_EQ(initializer_3.GetOptionalObservers(), - std::make_tuple(&optional_observer_1, &optional_observer_2, - &optional_observer_3)); - EXPECT_EQ(initializer_3.GetMandatoryObservers(), std::make_tuple()); - - auto initializer_4 = initializer_3.SetOptionalObservers(); - EXPECT_EQ(initializer_4.GetOptionalObservers(), std::make_tuple()); - EXPECT_EQ(initializer_4.GetMandatoryObservers(), std::make_tuple()); -} - -TEST_F(BaseAllocatorDispatcherInitializerTest, - VerifySettingMandatoryObservers) { - ObserverMock<int> mandatory_observer_1; - ObserverMock<float> mandatory_observer_2; - ObserverMock<size_t> mandatory_observer_3; - - auto initializer_1 = CreateInitializer().SetMandatoryObservers( - &mandatory_observer_1, &mandatory_observer_2); - EXPECT_EQ(initializer_1.GetMandatoryObservers(), - std::make_tuple(&mandatory_observer_1, &mandatory_observer_2)); - EXPECT_EQ(initializer_1.GetOptionalObservers(), std::make_tuple()); - - auto initializer_2 = - initializer_1.SetMandatoryObservers(&mandatory_observer_3); - EXPECT_EQ(initializer_2.GetMandatoryObservers(), - std::make_tuple(&mandatory_observer_3)); - EXPECT_EQ(initializer_2.GetOptionalObservers(), std::make_tuple()); - - auto initializer_3 = initializer_2.SetMandatoryObservers(); - EXPECT_EQ(initializer_3.GetMandatoryObservers(), std::make_tuple()); - EXPECT_EQ(initializer_3.GetOptionalObservers(), std::make_tuple()); -} - -TEST_F(BaseAllocatorDispatcherInitializerTest, VerifyAddingMandatoryObservers) { - ObserverMock<int> mandatory_observer_1; - ObserverMock<float> mandatory_observer_2; - ObserverMock<size_t> mandatory_observer_3; - - auto initializer_1 = CreateInitializer().AddMandatoryObservers( - &mandatory_observer_1, &mandatory_observer_2); - EXPECT_EQ(initializer_1.GetMandatoryObservers(), - std::make_tuple(&mandatory_observer_1, &mandatory_observer_2)); - EXPECT_EQ(initializer_1.GetOptionalObservers(), std::make_tuple()); - - auto initializer_2 = - initializer_1.AddMandatoryObservers(&mandatory_observer_3); - EXPECT_EQ(initializer_2.GetMandatoryObservers(), - std::make_tuple(&mandatory_observer_1, &mandatory_observer_2, - &mandatory_observer_3)); - EXPECT_EQ(initializer_2.GetOptionalObservers(), std::make_tuple()); - - auto initializer_3 = initializer_2.AddMandatoryObservers(); - EXPECT_EQ(initializer_3.GetMandatoryObservers(), - std::make_tuple(&mandatory_observer_1, &mandatory_observer_2, - &mandatory_observer_3)); - EXPECT_EQ(initializer_3.GetOptionalObservers(), std::make_tuple()); - - auto initializer_4 = initializer_3.SetMandatoryObservers(); - EXPECT_EQ(initializer_4.GetMandatoryObservers(), std::make_tuple()); - EXPECT_EQ(initializer_4.GetOptionalObservers(), std::make_tuple()); -} - -TEST_F(BaseAllocatorDispatcherInitializerTest, VerifyBasicInitialization) { - ObserverMock<int> optional_observer_1; - ObserverMock<float> optional_observer_2; - ObserverMock<size_t> mandatory_observer_1; - ObserverMock<double> mandatory_observer_2; - - testing::Dispatcher test_dispatcher; - - CreateInitializer() - .SetMandatoryObservers(&mandatory_observer_1, &mandatory_observer_2) - .SetOptionalObservers(&optional_observer_1, &optional_observer_2) - .DoInitialize(test_dispatcher); - - const auto observer_ptrs = - std::make_tuple(&mandatory_observer_1, &mandatory_observer_2, - &optional_observer_1, &optional_observer_2); - - EXPECT_EQ(1ul, test_dispatcher.GetInitCounterForObservers(observer_ptrs)); -} - -TEST_F(BaseAllocatorDispatcherInitializerTest, - VerifyInitializationWithMandatoryNullObservers) { - ObserverMock<int> optional_observer_1; - ObserverMock<float> optional_observer_2; - ObserverMock<size_t> mandatory_observer; - ObserverMock<double>* mandatory_null_observer = nullptr; - - testing::Dispatcher test_dispatcher; - - CreateInitializer() - .SetMandatoryObservers(&mandatory_observer, mandatory_null_observer) - .SetOptionalObservers(&optional_observer_1, &optional_observer_2) - .DoInitialize(test_dispatcher); - - // For mandatory observers being null we expect them to be passed straight - // down to the dispatcher, which will then perform a check of ALL observers. - const auto valid_observer_ptrs = - std::make_tuple(&mandatory_observer, mandatory_null_observer, - &optional_observer_1, &optional_observer_2); - - EXPECT_EQ(1ul, test_dispatcher.GetTotalInitCounter()); - EXPECT_EQ(1ul, - test_dispatcher.GetInitCounterForObservers(valid_observer_ptrs)); -} - -TEST_F(BaseAllocatorDispatcherInitializerTest, - VerifyInitializationWithOptionalNullObservers) { - ObserverMock<int> optional_observer; - ObserverMock<float>* optional_null_observer = nullptr; - ObserverMock<size_t> mandatory_observer_1; - ObserverMock<double> mandatory_observer_2; - - testing::Dispatcher test_dispatcher; - - CreateInitializer() - .SetMandatoryObservers(&mandatory_observer_1, &mandatory_observer_2) - .SetOptionalObservers(&optional_observer, optional_null_observer) - .DoInitialize(test_dispatcher); - - const auto valid_observer_ptrs = std::make_tuple( - &mandatory_observer_1, &mandatory_observer_2, &optional_observer); - - EXPECT_EQ(1ul, test_dispatcher.GetTotalInitCounter()); - EXPECT_EQ(1ul, - test_dispatcher.GetInitCounterForObservers(valid_observer_ptrs)); -} - -} // namespace base::allocator::dispatcher \ No newline at end of file
diff --git a/base/allocator/dispatcher/internal/dispatch_data.cc b/base/allocator/dispatcher/internal/dispatch_data.cc deleted file mode 100644 index 2b6b7f5..0000000 --- a/base/allocator/dispatcher/internal/dispatch_data.cc +++ /dev/null
@@ -1,42 +0,0 @@ -// 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. - -#include "base/allocator/dispatcher/internal/dispatch_data.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" - -namespace base::allocator::dispatcher::internal { - -#if BUILDFLAG(USE_PARTITION_ALLOC) - -DispatchData& DispatchData::SetAllocationObserverHooks( - AllocationObserverHook* allocation_observer_hook, - FreeObserverHook* free_observer_hook) { - allocation_observer_hook_ = allocation_observer_hook; - free_observer_hook_ = free_observer_hook; - - return *this; -} - -DispatchData::AllocationObserverHook* DispatchData::GetAllocationObserverHook() - const { - return allocation_observer_hook_; -} - -DispatchData::FreeObserverHook* DispatchData::GetFreeObserverHook() const { - return free_observer_hook_; -} -#endif - -#if BUILDFLAG(USE_ALLOCATOR_SHIM) -DispatchData& DispatchData::SetAllocatorDispatch( - AllocatorDispatch* allocator_dispatch) { - allocator_dispatch_ = allocator_dispatch; - return *this; -} - -AllocatorDispatch* DispatchData::GetAllocatorDispatch() const { - return allocator_dispatch_; -} -#endif -} // namespace base::allocator::dispatcher::internal
diff --git a/base/allocator/dispatcher/internal/dispatch_data.h b/base/allocator/dispatcher/internal/dispatch_data.h deleted file mode 100644 index 64931e6..0000000 --- a/base/allocator/dispatcher/internal/dispatch_data.h +++ /dev/null
@@ -1,61 +0,0 @@ -// 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. - -#ifndef BASE_ALLOCATOR_DISPATCHER_INTERNAL_DISPATCH_DATA_H_ -#define BASE_ALLOCATOR_DISPATCHER_INTERNAL_DISPATCH_DATA_H_ - -#include "base/allocator/buildflags.h" -#if defined(COBALT_PENDING_CLEAN_UP) -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#endif -#include "base/base_export.h" -#include "build/build_config.h" - -#if BUILDFLAG(USE_PARTITION_ALLOC) -#include "base/allocator/partition_allocator/partition_alloc.h" -#endif - -#if BUILDFLAG(USE_ALLOCATOR_SHIM) -#include "base/allocator/partition_allocator/shim/allocator_shim.h" -#endif - -namespace base::allocator::dispatcher::internal { - -#if BUILDFLAG(USE_ALLOCATOR_SHIM) -using allocator_shim::AllocatorDispatch; -#endif - -// A simple utility class to pass all the information required to properly hook -// into the memory allocation subsystems from DispatcherImpl to the Dispatcher. -struct BASE_EXPORT DispatchData { -#if BUILDFLAG(USE_PARTITION_ALLOC) - using AllocationObserverHook = - partition_alloc::PartitionAllocHooks::AllocationObserverHook; - using FreeObserverHook = - partition_alloc::PartitionAllocHooks::FreeObserverHook; - - DispatchData& SetAllocationObserverHooks(AllocationObserverHook*, - FreeObserverHook*); - AllocationObserverHook* GetAllocationObserverHook() const; - FreeObserverHook* GetFreeObserverHook() const; - - private: - AllocationObserverHook* allocation_observer_hook_ = nullptr; - FreeObserverHook* free_observer_hook_ = nullptr; - - public: -#endif - -#if BUILDFLAG(USE_ALLOCATOR_SHIM) - DispatchData& SetAllocatorDispatch(AllocatorDispatch* allocator_dispatch); - AllocatorDispatch* GetAllocatorDispatch() const; - - private: - AllocatorDispatch* allocator_dispatch_ = nullptr; -#endif -}; - -} // namespace base::allocator::dispatcher::internal - -#endif
diff --git a/base/allocator/dispatcher/internal/dispatcher_internal.h b/base/allocator/dispatcher/internal/dispatcher_internal.h deleted file mode 100644 index 7b62fb0..0000000 --- a/base/allocator/dispatcher/internal/dispatcher_internal.h +++ /dev/null
@@ -1,347 +0,0 @@ -// 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. - -#ifndef BASE_ALLOCATOR_DISPATCHER_INTERNAL_DISPATCHER_INTERNAL_H_ -#define BASE_ALLOCATOR_DISPATCHER_INTERNAL_DISPATCHER_INTERNAL_H_ - -#include "base/allocator/buildflags.h" -#include "base/allocator/dispatcher/configuration.h" -#include "base/allocator/dispatcher/internal/dispatch_data.h" -#include "base/allocator/dispatcher/internal/tools.h" -#include "base/allocator/dispatcher/subsystem.h" -#if !defined(COBALT_PENDING_CLEAN_UP) -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#endif -#include "base/check.h" -#include "base/compiler_specific.h" -#include "build/build_config.h" - -#if BUILDFLAG(USE_PARTITION_ALLOC) -#include "base/allocator/partition_allocator/partition_alloc.h" -#endif - -#if BUILDFLAG(USE_ALLOCATOR_SHIM) -#include "base/allocator/partition_allocator/shim/allocator_shim.h" -#endif - -#include <tuple> - -namespace base::allocator::dispatcher::internal { - -#if BUILDFLAG(USE_ALLOCATOR_SHIM) -using allocator_shim::AllocatorDispatch; -#endif - -template <typename CheckObserverPredicate, - typename... ObserverTypes, - size_t... Indices> -void inline PerformObserverCheck(const std::tuple<ObserverTypes...>& observers, - std::index_sequence<Indices...>, - CheckObserverPredicate check_observer) { - ([](bool b) { DCHECK(b); }(check_observer(std::get<Indices>(observers))), - ...); -} - -template <typename... ObserverTypes, size_t... Indices> -ALWAYS_INLINE void PerformAllocationNotification( - const std::tuple<ObserverTypes...>& observers, - std::index_sequence<Indices...>, - void* address, - size_t size, - AllocationSubsystem subSystem, - const char* type_name) { - ((std::get<Indices>(observers)->OnAllocation(address, size, subSystem, - type_name)), - ...); -} - -template <typename... ObserverTypes, size_t... Indices> -ALWAYS_INLINE void PerformFreeNotification( - const std::tuple<ObserverTypes...>& observers, - std::index_sequence<Indices...>, - void* address) { - ((std::get<Indices>(observers)->OnFree(address)), ...); -} - -// DispatcherImpl provides hooks into the various memory subsystems. These hooks -// are responsible for dispatching any notification to the observers. -// In order to provide as many information on the exact type of the observer and -// prevent any conditional jumps in the hot allocation path, observers are -// stored in a std::tuple. DispatcherImpl performs a CHECK at initialization -// time to ensure they are valid. -template <typename... ObserverTypes> -struct DispatcherImpl { - using AllObservers = std::index_sequence_for<ObserverTypes...>; - - template <std::enable_if_t< - internal::LessEqual(sizeof...(ObserverTypes), - configuration::kMaximumNumberOfObservers), - bool> = true> - static DispatchData GetNotificationHooks( - std::tuple<ObserverTypes*...> observers) { - s_observers = std::move(observers); - - PerformObserverCheck(s_observers, AllObservers{}, IsValidObserver{}); - - return CreateDispatchData(); - } - - private: - static DispatchData CreateDispatchData() { - return DispatchData() -#if BUILDFLAG(USE_PARTITION_ALLOC) - .SetAllocationObserverHooks(&PartitionAllocatorAllocationHook, - &PartitionAllocatorFreeHook) -#endif -#if BUILDFLAG(USE_ALLOCATOR_SHIM) - .SetAllocatorDispatch(&allocator_dispatch_) -#endif - ; - } - -#if BUILDFLAG(USE_PARTITION_ALLOC) - static void PartitionAllocatorAllocationHook(void* address, - size_t size, - const char* type_name) { - DoNotifyAllocation(address, size, AllocationSubsystem::kPartitionAllocator, - type_name); - } - - static void PartitionAllocatorFreeHook(void* address) { - DoNotifyFree(address); - } -#endif - -#if BUILDFLAG(USE_ALLOCATOR_SHIM) - static void* AllocFn(const AllocatorDispatch* self, - size_t size, - void* context) { - void* const address = self->next->alloc_function(self->next, size, context); - - DoNotifyAllocation(address, size, AllocationSubsystem::kAllocatorShim); - - return address; - } - - static void* AllocUncheckedFn(const AllocatorDispatch* self, - size_t size, - void* context) { - void* const address = - self->next->alloc_unchecked_function(self->next, size, context); - - DoNotifyAllocation(address, size, AllocationSubsystem::kAllocatorShim); - - return address; - } - - static void* AllocZeroInitializedFn(const AllocatorDispatch* self, - size_t n, - size_t size, - void* context) { - void* const address = self->next->alloc_zero_initialized_function( - self->next, n, size, context); - - DoNotifyAllocation(address, n * size, AllocationSubsystem::kAllocatorShim); - - return address; - } - - static void* AllocAlignedFn(const AllocatorDispatch* self, - size_t alignment, - size_t size, - void* context) { - void* const address = self->next->alloc_aligned_function( - self->next, alignment, size, context); - - DoNotifyAllocation(address, size, AllocationSubsystem::kAllocatorShim); - - return address; - } - - static void* ReallocFn(const AllocatorDispatch* self, - void* address, - size_t size, - void* context) { - // Note: size == 0 actually performs free. - DoNotifyFree(address); - void* const reallocated_address = - self->next->realloc_function(self->next, address, size, context); - - DoNotifyAllocation(reallocated_address, size, - AllocationSubsystem::kAllocatorShim); - - return reallocated_address; - } - - static void FreeFn(const AllocatorDispatch* self, - void* address, - void* context) { - // Note: The RecordFree should be called before free_function (here and in - // other places). That is because observers need to handle the allocation - // being freed before calling free_function, as once the latter is executed - // the address becomes available and can be allocated by another thread. - // That would be racy otherwise. - DoNotifyFree(address); - self->next->free_function(self->next, address, context); - } - - static size_t GetSizeEstimateFn(const AllocatorDispatch* self, - void* address, - void* context) { - return self->next->get_size_estimate_function(self->next, address, context); - } - - static bool ClaimedAddressFn(const AllocatorDispatch* self, - void* address, - void* context) { - return self->next->claimed_address_function(self->next, address, context); - } - - static unsigned BatchMallocFn(const AllocatorDispatch* self, - size_t size, - void** results, - unsigned num_requested, - void* context) { - unsigned const num_allocated = self->next->batch_malloc_function( - self->next, size, results, num_requested, context); - for (unsigned i = 0; i < num_allocated; ++i) { - DoNotifyAllocation(results[i], size, AllocationSubsystem::kAllocatorShim); - } - return num_allocated; - } - - static void BatchFreeFn(const AllocatorDispatch* self, - void** to_be_freed, - unsigned num_to_be_freed, - void* context) { - // Note: The code doesn't need to protect from recursions using - // ReentryGuard, see ReallocFn for details. - for (unsigned i = 0; i < num_to_be_freed; ++i) { - DoNotifyFree(to_be_freed[i]); - } - self->next->batch_free_function(self->next, to_be_freed, num_to_be_freed, - context); - } - - static void FreeDefiniteSizeFn(const AllocatorDispatch* self, - void* address, - size_t size, - void* context) { - DoNotifyFree(address); - self->next->free_definite_size_function(self->next, address, size, context); - } - - static void TryFreeDefaultFn(const AllocatorDispatch* self, - void* address, - void* context) { - DoNotifyFree(address); - self->next->try_free_default_function(self->next, address, context); - } - - static void* AlignedMallocFn(const AllocatorDispatch* self, - size_t size, - size_t alignment, - void* context) { - void* const address = self->next->aligned_malloc_function( - self->next, size, alignment, context); - - DoNotifyAllocation(address, size, AllocationSubsystem::kAllocatorShim); - - return address; - } - - static void* AlignedReallocFn(const AllocatorDispatch* self, - void* address, - size_t size, - size_t alignment, - void* context) { - // Note: size == 0 actually performs free. - DoNotifyFree(address); - address = self->next->aligned_realloc_function(self->next, address, size, - alignment, context); - - DoNotifyAllocation(address, size, AllocationSubsystem::kAllocatorShim); - - return address; - } - - static void AlignedFreeFn(const AllocatorDispatch* self, - void* address, - void* context) { - DoNotifyFree(address); - self->next->aligned_free_function(self->next, address, context); - } - - static AllocatorDispatch allocator_dispatch_; -#endif - - ALWAYS_INLINE static void DoNotifyAllocation( - void* address, - size_t size, - AllocationSubsystem subSystem, - const char* type_name = nullptr) { - PerformAllocationNotification(s_observers, AllObservers{}, address, size, - subSystem, type_name); - } - - ALWAYS_INLINE static void DoNotifyFree(void* address) { - PerformFreeNotification(s_observers, AllObservers{}, address); - } - - static std::tuple<ObserverTypes*...> s_observers; -}; - -template <typename... ObserverTypes> -std::tuple<ObserverTypes*...> DispatcherImpl<ObserverTypes...>::s_observers; - -#if BUILDFLAG(USE_ALLOCATOR_SHIM) -template <typename... ObserverTypes> -AllocatorDispatch DispatcherImpl<ObserverTypes...>::allocator_dispatch_ = { - &AllocFn, - &AllocUncheckedFn, - &AllocZeroInitializedFn, - &AllocAlignedFn, - &ReallocFn, - &FreeFn, - &GetSizeEstimateFn, - &ClaimedAddressFn, - &BatchMallocFn, - &BatchFreeFn, - &FreeDefiniteSizeFn, - &TryFreeDefaultFn, - &AlignedMallocFn, - &AlignedReallocFn, - &AlignedFreeFn, - nullptr}; -#endif - -// Specialization of DispatcherImpl in case we have no observers to notify. In -// this special case we return a set of null pointers as the Dispatcher must not -// install any hooks at all. -template <> -struct DispatcherImpl<> { - static DispatchData GetNotificationHooks(std::tuple<> /*observers*/) { - return DispatchData() -#if BUILDFLAG(USE_PARTITION_ALLOC) - .SetAllocationObserverHooks(nullptr, nullptr) -#endif -#if BUILDFLAG(USE_ALLOCATOR_SHIM) - .SetAllocatorDispatch(nullptr) -#endif - ; - } -}; - -// A little utility function that helps using DispatcherImpl by providing -// automated type deduction for templates. -template <typename... ObserverTypes> -inline DispatchData GetNotificationHooks( - std::tuple<ObserverTypes*...> observers) { - return DispatcherImpl<ObserverTypes...>::GetNotificationHooks( - std::move(observers)); -} - -} // namespace base::allocator::dispatcher::internal - -#endif // BASE_ALLOCATOR_DISPATCHER_INTERNAL_DISPATCHER_INTERNAL_H_
diff --git a/base/allocator/dispatcher/internal/dispatcher_internal_unittest.cc b/base/allocator/dispatcher/internal/dispatcher_internal_unittest.cc deleted file mode 100644 index 4119b22..0000000 --- a/base/allocator/dispatcher/internal/dispatcher_internal_unittest.cc +++ /dev/null
@@ -1,550 +0,0 @@ -// 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. - -#include "base/allocator/dispatcher/internal/dispatcher_internal.h" -#include "base/allocator/buildflags.h" -#include "base/allocator/dispatcher/testing/dispatcher_test.h" -#include "base/allocator/dispatcher/testing/observer_mock.h" -#include "base/allocator/dispatcher/testing/tools.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/dcheck_is_on.h" -#include "testing/gtest/include/gtest/gtest.h" - -#include <tuple> -#include <utility> - -using ::base::allocator::dispatcher::configuration::kMaximumNumberOfObservers; -using ::base::allocator::dispatcher::testing::CreateTupleOfPointers; -using ::base::allocator::dispatcher::testing::DispatcherTest; -using ::testing::_; -using ::testing::InSequence; - -namespace base::allocator::dispatcher::internal { - -namespace { - -struct AllocationEventDispatcherInternalTest : public DispatcherTest { - static void* GetAllocatedAddress() { - return reinterpret_cast<void*>(0x12345678); - } - static unsigned int GetAllocatedSize() { return 35; } - static unsigned int GetEstimatedSize() { return 77; } - static void* GetFreedAddress() { - return reinterpret_cast<void*>(0x876543210); - } - -#if BUILDFLAG(USE_ALLOCATOR_SHIM) - AllocatorDispatch* GetNextAllocatorDispatch() { return &allocator_dispatch_; } - static void* alloc_function(const AllocatorDispatch*, size_t, void*) { - return GetAllocatedAddress(); - } - static void* alloc_unchecked_function(const AllocatorDispatch*, - size_t, - void*) { - return GetAllocatedAddress(); - } - static void* alloc_zero_initialized_function(const AllocatorDispatch*, - size_t, - size_t, - void*) { - return GetAllocatedAddress(); - } - static void* alloc_aligned_function(const AllocatorDispatch*, - size_t, - size_t, - void*) { - return GetAllocatedAddress(); - } - static void* realloc_function(const AllocatorDispatch*, - void*, - size_t, - void*) { - return GetAllocatedAddress(); - } - static size_t get_size_estimate_function(const AllocatorDispatch*, - void*, - void*) { - return GetEstimatedSize(); - } - static bool claimed_address_function(const AllocatorDispatch*, void*, void*) { - return GetEstimatedSize(); - } - static unsigned batch_malloc_function(const AllocatorDispatch*, - size_t, - void**, - unsigned num_requested, - void*) { - return num_requested; - } - static void* aligned_malloc_function(const AllocatorDispatch*, - size_t, - size_t, - void*) { - return GetAllocatedAddress(); - } - static void* aligned_realloc_function(const AllocatorDispatch*, - void*, - size_t, - size_t, - void*) { - return GetAllocatedAddress(); - } - - AllocatorDispatch allocator_dispatch_ = { - &alloc_function, - &alloc_unchecked_function, - &alloc_zero_initialized_function, - &alloc_aligned_function, - &realloc_function, - [](const AllocatorDispatch*, void*, void*) {}, - &get_size_estimate_function, - &claimed_address_function, - &batch_malloc_function, - [](const AllocatorDispatch*, void**, unsigned, void*) {}, - [](const AllocatorDispatch*, void*, size_t, void*) {}, - [](const AllocatorDispatch*, void*, void*) {}, - &aligned_malloc_function, - &aligned_realloc_function, - [](const AllocatorDispatch*, void*, void*) {}}; -#endif -}; - -} // namespace - -using ::testing::NaggyMock; -using ::testing::StrictMock; - -using ObserverMock = StrictMock<testing::ObserverMock<>>; - -#if defined(GTEST_HAS_DEATH_TEST) && GTEST_HAS_DEATH_TEST && DCHECK_IS_ON() -TEST(AllocationEventDispatcherInternalDeathTest, - VerifyDeathWhenObserverIsNull) { - testing::ObserverMock<int> observer_1; - testing::ObserverMock<float> observer_2; - testing::ObserverMock<size_t>* null_observer = nullptr; - testing::ObserverMock<double> observer_3; - - const auto observer_ptrs = - std::make_tuple(&observer_1, &observer_2, null_observer, &observer_3); - - EXPECT_DEATH({ GetNotificationHooks(observer_ptrs); }, ""); -} -#endif // defined(GTEST_HAS_DEATH_TEST) && GTEST_HAS_DEATH_TEST && - // DCHECK_IS_ON() - -#if BUILDFLAG(USE_PARTITION_ALLOC) -TEST_F(AllocationEventDispatcherInternalTest, - VerifyPartitionAllocatorHooksAreSet) { - std::array<ObserverMock, 1> observers; - - const auto dispatch_data = - GetNotificationHooks(CreateTupleOfPointers(observers)); - - EXPECT_NE(nullptr, dispatch_data.GetAllocationObserverHook()); - EXPECT_NE(nullptr, dispatch_data.GetFreeObserverHook()); -} - -TEST_F(AllocationEventDispatcherInternalTest, - VerifyPartitionAllocatorHooksAreNullWhenNoObservers) { - const auto dispatch_data = GetNotificationHooks(std::make_tuple()); - - EXPECT_EQ(nullptr, dispatch_data.GetAllocationObserverHook()); - EXPECT_EQ(nullptr, dispatch_data.GetFreeObserverHook()); -} - -TEST_F(AllocationEventDispatcherInternalTest, - VerifyPartitionAllocatorAllocationHooksTriggerCorrectly) { - std::array<ObserverMock, kMaximumNumberOfObservers> observers; - - for (auto& mock : observers) { - EXPECT_CALL(mock, OnAllocation(_, _, _, _)).Times(0); - EXPECT_CALL(mock, OnAllocation(this, sizeof(*this), _, _)).Times(1); - EXPECT_CALL(mock, OnFree(_)).Times(0); - } - - const auto dispatch_data = - GetNotificationHooks(CreateTupleOfPointers(observers)); - - dispatch_data.GetAllocationObserverHook()(this, sizeof(*this), nullptr); -} - -TEST_F(AllocationEventDispatcherInternalTest, - VerifyPartitionAllocatorFreeHooksTriggerCorrectly) { - std::array<ObserverMock, kMaximumNumberOfObservers> observers; - - for (auto& mock : observers) { - EXPECT_CALL(mock, OnAllocation(_, _, _, _)).Times(0); - EXPECT_CALL(mock, OnFree(_)).Times(0); - EXPECT_CALL(mock, OnFree(this)).Times(1); - } - - const auto dispatch_data = - GetNotificationHooks(CreateTupleOfPointers(observers)); - - dispatch_data.GetFreeObserverHook()(this); -} -#endif - -#if BUILDFLAG(USE_ALLOCATOR_SHIM) -TEST_F(AllocationEventDispatcherInternalTest, VerifyAllocatorShimDataIsSet) { - std::array<ObserverMock, 1> observers; - - const auto dispatch_data = - GetNotificationHooks(CreateTupleOfPointers(observers)); - const auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch(); - EXPECT_NE(nullptr, allocator_dispatch); - EXPECT_NE(nullptr, allocator_dispatch->alloc_function); - EXPECT_NE(nullptr, allocator_dispatch->alloc_unchecked_function); - EXPECT_NE(nullptr, allocator_dispatch->alloc_zero_initialized_function); - EXPECT_NE(nullptr, allocator_dispatch->alloc_aligned_function); - EXPECT_NE(nullptr, allocator_dispatch->realloc_function); - EXPECT_NE(nullptr, allocator_dispatch->free_function); - EXPECT_NE(nullptr, allocator_dispatch->get_size_estimate_function); - EXPECT_NE(nullptr, allocator_dispatch->claimed_address_function); - EXPECT_NE(nullptr, allocator_dispatch->batch_malloc_function); - EXPECT_NE(nullptr, allocator_dispatch->batch_free_function); - EXPECT_NE(nullptr, allocator_dispatch->free_definite_size_function); - EXPECT_NE(nullptr, allocator_dispatch->try_free_default_function); - EXPECT_NE(nullptr, allocator_dispatch->aligned_malloc_function); - EXPECT_NE(nullptr, allocator_dispatch->aligned_realloc_function); - EXPECT_NE(nullptr, allocator_dispatch->aligned_free_function); -} - -TEST_F(AllocationEventDispatcherInternalTest, - VerifyAllocatorShimDataIsNullWhenNoObservers) { - const auto dispatch_data = GetNotificationHooks(std::make_tuple()); - - EXPECT_EQ(nullptr, dispatch_data.GetAllocatorDispatch()); -} - -TEST_F(AllocationEventDispatcherInternalTest, - VerifyAllocatorShimHooksTriggerCorrectly_alloc_function) { - std::array<ObserverMock, kMaximumNumberOfObservers> observers; - - for (auto& mock : observers) { - EXPECT_CALL(mock, OnAllocation(_, _, _, _)).Times(0); - EXPECT_CALL(mock, OnAllocation(GetAllocatedAddress(), GetAllocatedSize(), - AllocationSubsystem::kAllocatorShim, _)) - .Times(1); - EXPECT_CALL(mock, OnFree(_)).Times(0); - } - - auto const dispatch_data = - GetNotificationHooks(CreateTupleOfPointers(observers)); - - auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch(); - allocator_dispatch->next = GetNextAllocatorDispatch(); - - auto* const allocated_address = allocator_dispatch->alloc_function( - allocator_dispatch, GetAllocatedSize(), nullptr); - - EXPECT_EQ(allocated_address, GetAllocatedAddress()); -} - -TEST_F(AllocationEventDispatcherInternalTest, - VerifyAllocatorShimHooksTriggerCorrectly_alloc_unchecked_function) { - std::array<ObserverMock, kMaximumNumberOfObservers> observers; - - for (auto& mock : observers) { - EXPECT_CALL(mock, OnAllocation(_, _, _, _)).Times(0); - EXPECT_CALL(mock, OnAllocation(GetAllocatedAddress(), GetAllocatedSize(), - AllocationSubsystem::kAllocatorShim, _)) - .Times(1); - EXPECT_CALL(mock, OnFree(_)).Times(0); - } - - auto const dispatch_data = - GetNotificationHooks(CreateTupleOfPointers(observers)); - - auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch(); - allocator_dispatch->next = GetNextAllocatorDispatch(); - - auto* const allocated_address = allocator_dispatch->alloc_unchecked_function( - allocator_dispatch, GetAllocatedSize(), nullptr); - - EXPECT_EQ(allocated_address, GetAllocatedAddress()); -} - -TEST_F( - AllocationEventDispatcherInternalTest, - VerifyAllocatorShimHooksTriggerCorrectly_alloc_zero_initialized_function) { - std::array<ObserverMock, kMaximumNumberOfObservers> observers; - constexpr int n = 8; - - for (auto& mock : observers) { - EXPECT_CALL(mock, OnAllocation(_, _, _, _)).Times(0); - EXPECT_CALL(mock, - OnAllocation(GetAllocatedAddress(), n * GetAllocatedSize(), - AllocationSubsystem::kAllocatorShim, _)) - .Times(1); - EXPECT_CALL(mock, OnFree(_)).Times(0); - } - - auto const dispatch_data = - GetNotificationHooks(CreateTupleOfPointers(observers)); - - auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch(); - allocator_dispatch->next = GetNextAllocatorDispatch(); - - auto* const allocated_address = - allocator_dispatch->alloc_zero_initialized_function( - allocator_dispatch, n, GetAllocatedSize(), nullptr); - - EXPECT_EQ(allocated_address, GetAllocatedAddress()); -} - -TEST_F(AllocationEventDispatcherInternalTest, - VerifyAllocatorShimHooksTriggerCorrectly_alloc_aligned_function) { - std::array<ObserverMock, kMaximumNumberOfObservers> observers; - - for (auto& mock : observers) { - EXPECT_CALL(mock, OnAllocation(_, _, _, _)).Times(0); - EXPECT_CALL(mock, OnAllocation(GetAllocatedAddress(), GetAllocatedSize(), - AllocationSubsystem::kAllocatorShim, _)) - .Times(1); - EXPECT_CALL(mock, OnFree(_)).Times(0); - } - - auto const dispatch_data = - GetNotificationHooks(CreateTupleOfPointers(observers)); - - auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch(); - allocator_dispatch->next = GetNextAllocatorDispatch(); - - auto* const allocated_address = allocator_dispatch->alloc_aligned_function( - allocator_dispatch, 2048, GetAllocatedSize(), nullptr); - - EXPECT_EQ(allocated_address, GetAllocatedAddress()); -} - -TEST_F(AllocationEventDispatcherInternalTest, - VerifyAllocatorShimHooksTriggerCorrectly_realloc_function) { - std::array<ObserverMock, kMaximumNumberOfObservers> observers; - - for (auto& mock : observers) { - InSequence execution_order; - - EXPECT_CALL(mock, OnFree(GetFreedAddress())).Times(1); - EXPECT_CALL(mock, OnAllocation(GetAllocatedAddress(), GetAllocatedSize(), - AllocationSubsystem::kAllocatorShim, _)) - .Times(1); - } - - auto const dispatch_data = - GetNotificationHooks(CreateTupleOfPointers(observers)); - - auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch(); - allocator_dispatch->next = GetNextAllocatorDispatch(); - - auto* const allocated_address = allocator_dispatch->realloc_function( - allocator_dispatch, GetFreedAddress(), GetAllocatedSize(), nullptr); - - EXPECT_EQ(allocated_address, GetAllocatedAddress()); -} - -TEST_F(AllocationEventDispatcherInternalTest, - VerifyAllocatorShimHooksTriggerCorrectly_free_function) { - std::array<ObserverMock, kMaximumNumberOfObservers> observers; - - for (auto& mock : observers) { - EXPECT_CALL(mock, OnFree(GetFreedAddress())).Times(1); - EXPECT_CALL(mock, OnAllocation(_, _, _, _)).Times(0); - } - - auto const dispatch_data = - GetNotificationHooks(CreateTupleOfPointers(observers)); - - auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch(); - allocator_dispatch->next = GetNextAllocatorDispatch(); - - allocator_dispatch->free_function(allocator_dispatch, GetFreedAddress(), - nullptr); -} - -TEST_F(AllocationEventDispatcherInternalTest, - VerifyAllocatorShimHooksTriggerCorrectly_get_size_estimate_function) { - std::array<ObserverMock, kMaximumNumberOfObservers> observers; - - for (auto& mock : observers) { - EXPECT_CALL(mock, OnFree(_)).Times(0); - EXPECT_CALL(mock, OnAllocation(_, _, _, _)).Times(0); - } - - auto const dispatch_data = - GetNotificationHooks(CreateTupleOfPointers(observers)); - - auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch(); - allocator_dispatch->next = GetNextAllocatorDispatch(); - - auto const estimated_size = allocator_dispatch->get_size_estimate_function( - allocator_dispatch, GetAllocatedAddress(), nullptr); - - EXPECT_EQ(estimated_size, GetEstimatedSize()); -} - -TEST_F(AllocationEventDispatcherInternalTest, - VerifyAllocatorShimHooksTriggerCorrectly_batch_malloc_function) { - constexpr size_t allocation_batch_size = 10; - std::array<ObserverMock, kMaximumNumberOfObservers> observers; - std::array<void*, allocation_batch_size> allocation_batch = {nullptr}; - - for (auto& mock : observers) { - EXPECT_CALL(mock, OnFree(_)).Times(0); - EXPECT_CALL(mock, OnAllocation(nullptr, GetAllocatedSize(), - AllocationSubsystem::kAllocatorShim, _)) - .Times(allocation_batch_size); - } - - auto const dispatch_data = - GetNotificationHooks(CreateTupleOfPointers(observers)); - - auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch(); - EXPECT_NE(allocator_dispatch->batch_malloc_function, nullptr); - - allocator_dispatch->next = GetNextAllocatorDispatch(); - - auto const number_allocated = allocator_dispatch->batch_malloc_function( - allocator_dispatch, GetAllocatedSize(), allocation_batch.data(), - allocation_batch_size, nullptr); - - EXPECT_EQ(number_allocated, allocation_batch_size); -} - -TEST_F(AllocationEventDispatcherInternalTest, - VerifyAllocatorShimHooksTriggerCorrectly_batch_free_function) { - constexpr size_t allocation_batch_size = 10; - std::array<ObserverMock, kMaximumNumberOfObservers> observers; - std::array<void*, allocation_batch_size> allocation_batch; - allocation_batch.fill(GetFreedAddress()); - - for (auto& mock : observers) { - EXPECT_CALL(mock, OnFree(GetFreedAddress())).Times(allocation_batch_size); - EXPECT_CALL(mock, OnAllocation(_, _, _, _)).Times(0); - } - - auto const dispatch_data = - GetNotificationHooks(CreateTupleOfPointers(observers)); - - auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch(); - EXPECT_NE(allocator_dispatch->batch_free_function, nullptr); - - allocator_dispatch->next = GetNextAllocatorDispatch(); - - allocator_dispatch->batch_free_function(allocator_dispatch, - allocation_batch.data(), - allocation_batch_size, nullptr); -} - -TEST_F(AllocationEventDispatcherInternalTest, - VerifyAllocatorShimHooksTriggerCorrectly_free_definite_size_function) { - std::array<ObserverMock, kMaximumNumberOfObservers> observers; - - for (auto& mock : observers) { - EXPECT_CALL(mock, OnFree(GetAllocatedAddress())).Times(1); - EXPECT_CALL(mock, OnAllocation(_, _, _, _)).Times(0); - } - - DispatchData const dispatch_data = - GetNotificationHooks(CreateTupleOfPointers(observers)); - - auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch(); - EXPECT_NE(allocator_dispatch->free_definite_size_function, nullptr); - - allocator_dispatch->next = GetNextAllocatorDispatch(); - - allocator_dispatch->free_definite_size_function( - allocator_dispatch, GetAllocatedAddress(), GetAllocatedSize(), nullptr); -} - -TEST_F(AllocationEventDispatcherInternalTest, - VerifyAllocatorShimHooksTriggerCorrectly_try_free_default_function) { - std::array<ObserverMock, kMaximumNumberOfObservers> observers; - - for (auto& mock : observers) { - EXPECT_CALL(mock, OnFree(GetAllocatedAddress())).Times(1); - EXPECT_CALL(mock, OnAllocation(_, _, _, _)).Times(0); - } - - DispatchData const dispatch_data = - GetNotificationHooks(CreateTupleOfPointers(observers)); - - auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch(); - EXPECT_NE(allocator_dispatch->try_free_default_function, nullptr); - - allocator_dispatch->next = GetNextAllocatorDispatch(); - - allocator_dispatch->try_free_default_function(allocator_dispatch, - GetAllocatedAddress(), nullptr); -} - -TEST_F(AllocationEventDispatcherInternalTest, - VerifyAllocatorShimHooksTriggerCorrectly_aligned_malloc_function) { - std::array<ObserverMock, kMaximumNumberOfObservers> observers; - - for (auto& mock : observers) { - EXPECT_CALL(mock, OnAllocation(_, _, _, _)).Times(0); - EXPECT_CALL(mock, OnAllocation(GetAllocatedAddress(), GetAllocatedSize(), - AllocationSubsystem::kAllocatorShim, _)) - .Times(1); - EXPECT_CALL(mock, OnFree(_)).Times(0); - } - - auto const dispatch_data = - GetNotificationHooks(CreateTupleOfPointers(observers)); - - auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch(); - allocator_dispatch->next = GetNextAllocatorDispatch(); - - auto* const allocated_address = allocator_dispatch->aligned_malloc_function( - allocator_dispatch, GetAllocatedSize(), 2048, nullptr); - - EXPECT_EQ(allocated_address, GetAllocatedAddress()); -} - -TEST_F(AllocationEventDispatcherInternalTest, - VerifyAllocatorShimHooksTriggerCorrectly_aligned_realloc_function) { - std::array<ObserverMock, kMaximumNumberOfObservers> observers; - - for (auto& mock : observers) { - InSequence execution_order; - - EXPECT_CALL(mock, OnFree(GetFreedAddress())).Times(1); - EXPECT_CALL(mock, OnAllocation(GetAllocatedAddress(), GetAllocatedSize(), - AllocationSubsystem::kAllocatorShim, _)) - .Times(1); - } - - auto const dispatch_data = - GetNotificationHooks(CreateTupleOfPointers(observers)); - - auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch(); - allocator_dispatch->next = GetNextAllocatorDispatch(); - - auto* const allocated_address = allocator_dispatch->aligned_realloc_function( - allocator_dispatch, GetFreedAddress(), GetAllocatedSize(), 2048, nullptr); - - EXPECT_EQ(allocated_address, GetAllocatedAddress()); -} - -TEST_F(AllocationEventDispatcherInternalTest, - VerifyAllocatorShimHooksTriggerCorrectly_aligned_free_function) { - std::array<ObserverMock, kMaximumNumberOfObservers> observers; - - for (auto& mock : observers) { - EXPECT_CALL(mock, OnFree(GetFreedAddress())).Times(1); - EXPECT_CALL(mock, OnAllocation(_, _, _, _)).Times(0); - } - - auto const dispatch_data = - GetNotificationHooks(CreateTupleOfPointers(observers)); - - auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch(); - allocator_dispatch->next = GetNextAllocatorDispatch(); - - allocator_dispatch->aligned_free_function(allocator_dispatch, - GetFreedAddress(), nullptr); -} - -#endif -} // namespace base::allocator::dispatcher::internal
diff --git a/base/allocator/dispatcher/internal/tools.h b/base/allocator/dispatcher/internal/tools.h deleted file mode 100644 index e270f48..0000000 --- a/base/allocator/dispatcher/internal/tools.h +++ /dev/null
@@ -1,29 +0,0 @@ -// 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. - -#ifndef BASE_ALLOCATOR_DISPATCHER_INTERNAL_TOOLS_H_ -#define BASE_ALLOCATOR_DISPATCHER_INTERNAL_TOOLS_H_ - -#include <cstddef> - -namespace base::allocator::dispatcher::internal { - -constexpr bool LessEqual(size_t lhs, size_t rhs) { - return lhs <= rhs; -} - -constexpr bool Equal(size_t lhs, size_t rhs) { - return lhs == rhs; -} - -struct IsValidObserver { - template <typename T> - constexpr bool operator()(T const* ptr) const noexcept { - return ptr != nullptr; - } -}; - -} // namespace base::allocator::dispatcher::internal - -#endif // BASE_ALLOCATOR_DISPATCHER_INTERNAL_DISPATCHER_H_ \ No newline at end of file
diff --git a/base/allocator/dispatcher/reentry_guard.cc b/base/allocator/dispatcher/reentry_guard.cc deleted file mode 100644 index 92a526a..0000000 --- a/base/allocator/dispatcher/reentry_guard.cc +++ /dev/null
@@ -1,61 +0,0 @@ -// 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. - -#include "base/allocator/dispatcher/reentry_guard.h" - -#include "base/check.h" -#include "base/compiler_specific.h" -#include "base/debug/crash_logging.h" -#include "base/strings/string_number_conversions.h" -#include "build/build_config.h" - -#if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_ANDROID) -#include <pthread.h> -#endif - -namespace base::allocator::dispatcher { - -#if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_ANDROID) -// pthread_key_t has different signedness on Mac and Android. Store the null -// value in a strongly-typed constant to avoid "comparison of integers of -// different signs" warnings when comparing with 0. -constexpr pthread_key_t kNullKey = 0; - -pthread_key_t ReentryGuard::entered_key_ = kNullKey; - -void ReentryGuard::InitTLSSlot() { - if (entered_key_ == kNullKey) { - int error = pthread_key_create(&entered_key_, nullptr); - CHECK(!error); - // Touch the TLS slot immediately to force any allocations. - // TODO(https://crbug.com/1411454): Use this technique to avoid allocations - // in PoissonAllocationSampler::ScopedMuteThreadSamples, which will make - // ReentryGuard redundant. - pthread_setspecific(entered_key_, nullptr); - } - - DCHECK_NE(entered_key_, kNullKey); -} - -#else - -void ReentryGuard::InitTLSSlot() {} - -#endif - -void ReentryGuard::RecordTLSSlotToCrashKey() { - // Record the key in crash dumps to detect when it's higher than 32 - // (PTHREAD_KEY_2NDLEVEL_SIZE). - // TODO(crbug.com/1411454): Remove this after diagnosing reentry crashes. - static auto* const crash_key = base::debug::AllocateCrashKeyString( - "reentry_guard_tls_slot", base::debug::CrashKeySize::Size32); - -#if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_ANDROID) - base::debug::SetCrashKeyString(crash_key, base::NumberToString(entered_key_)); -#else - base::debug::SetCrashKeyString(crash_key, "unused"); -#endif -} - -} // namespace base::allocator::dispatcher
diff --git a/base/allocator/dispatcher/reentry_guard.h b/base/allocator/dispatcher/reentry_guard.h deleted file mode 100644 index 986cfd2..0000000 --- a/base/allocator/dispatcher/reentry_guard.h +++ /dev/null
@@ -1,75 +0,0 @@ -// 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. - -#ifndef BASE_ALLOCATOR_DISPATCHER_REENTRY_GUARD_H_ -#define BASE_ALLOCATOR_DISPATCHER_REENTRY_GUARD_H_ - -#include "base/base_export.h" -#include "base/check.h" -#include "base/compiler_specific.h" -#include "build/build_config.h" - -#if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_ANDROID) -#include <pthread.h> -#endif - -namespace base::allocator::dispatcher { - -#if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_ANDROID) - -// The macOS implementation of libmalloc sometimes calls malloc recursively, -// delegating allocations between zones. That causes our hooks being called -// twice. The scoped guard allows us to detect that. -// -// Besides that the implementations of thread_local on macOS and Android -// seem to allocate memory lazily on the first access to thread_local variables -// (and on Android at least thread_local is implemented on top of pthread so is -// strictly worse for performance). Make use of pthread TLS instead of C++ -// thread_local there. -struct BASE_EXPORT ReentryGuard { - ALWAYS_INLINE ReentryGuard() : allowed_(!pthread_getspecific(entered_key_)) { - pthread_setspecific(entered_key_, reinterpret_cast<void*>(true)); - } - - ALWAYS_INLINE ~ReentryGuard() { - if (LIKELY(allowed_)) - pthread_setspecific(entered_key_, nullptr); - } - - explicit operator bool() const noexcept { return allowed_; } - - // This function must be called before installing any allocator hooks because - // some TLS implementations may allocate (eg. glibc will require a malloc call - // to allocate storage for a higher slot number (>= PTHREAD_KEY_2NDLEVEL_SIZE - // == 32). This touches the thread-local storage so that any malloc happens - // before installing the hooks. - static void InitTLSSlot(); - - // InitTLSSlot() is called before crash keys are available. At some point - // after SetCrashKeyImplementation() is called, this function should be - // called to record `entered_key_` to a crash key for debugging. This may - // allocate so it must not be called from inside an allocator hook. - static void RecordTLSSlotToCrashKey(); - - private: - static pthread_key_t entered_key_; - const bool allowed_; -}; - -#else - -// Use [[maybe_unused]] as this lightweight stand-in for the more heavyweight -// ReentryGuard above will otherwise trigger the "unused code" warnings. -struct [[maybe_unused]] BASE_EXPORT ReentryGuard { - constexpr explicit operator bool() const noexcept { return true; } - - static void InitTLSSlot(); - static void RecordTLSSlotToCrashKey(); -}; - -#endif - -} // namespace base::allocator::dispatcher - -#endif // BASE_ALLOCATOR_DISPATCHER_REENTRY_GUARD_H_
diff --git a/base/allocator/dispatcher/standard_hooks.cc b/base/allocator/dispatcher/standard_hooks.cc deleted file mode 100644 index 2d29d08..0000000 --- a/base/allocator/dispatcher/standard_hooks.cc +++ /dev/null
@@ -1,242 +0,0 @@ -// 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 "base/allocator/dispatcher/standard_hooks.h" - -#include "base/allocator/buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/shim/allocator_shim.h" -#include "base/sampling_heap_profiler/poisson_allocation_sampler.h" - -#if !BUILDFLAG(USE_ALLOCATION_EVENT_DISPATCHER) -#if BUILDFLAG(USE_ALLOCATOR_SHIM) -namespace base::allocator::dispatcher::allocator_shim_details { -namespace { - -using allocator_shim::AllocatorDispatch; - -void* AllocFn(const AllocatorDispatch* self, size_t size, void* context) { - void* address = self->next->alloc_function(self->next, size, context); - - PoissonAllocationSampler::RecordAlloc( - address, size, AllocationSubsystem::kAllocatorShim, nullptr); - - return address; -} - -void* AllocUncheckedFn(const AllocatorDispatch* self, - size_t size, - void* context) { - void* address = - self->next->alloc_unchecked_function(self->next, size, context); - - PoissonAllocationSampler::RecordAlloc( - address, size, AllocationSubsystem::kAllocatorShim, nullptr); - - return address; -} - -void* AllocZeroInitializedFn(const AllocatorDispatch* self, - size_t n, - size_t size, - void* context) { - void* address = - self->next->alloc_zero_initialized_function(self->next, n, size, context); - - PoissonAllocationSampler::RecordAlloc( - address, n * size, AllocationSubsystem::kAllocatorShim, nullptr); - - return address; -} - -void* AllocAlignedFn(const AllocatorDispatch* self, - size_t alignment, - size_t size, - void* context) { - void* address = - self->next->alloc_aligned_function(self->next, alignment, size, context); - - PoissonAllocationSampler::RecordAlloc( - address, size, AllocationSubsystem::kAllocatorShim, nullptr); - - return address; -} - -void* ReallocFn(const AllocatorDispatch* self, - void* address, - size_t size, - void* context) { - // Note: size == 0 actually performs free. - PoissonAllocationSampler::RecordFree(address); - address = self->next->realloc_function(self->next, address, size, context); - - PoissonAllocationSampler::RecordAlloc( - address, size, AllocationSubsystem::kAllocatorShim, nullptr); - - return address; -} - -void FreeFn(const AllocatorDispatch* self, void* address, void* context) { - // Note: The RecordFree should be called before free_function - // (here and in other places). - // That is because we need to remove the recorded allocation sample before - // free_function, as once the latter is executed the address becomes available - // and can be allocated by another thread. That would be racy otherwise. - PoissonAllocationSampler::RecordFree(address); - self->next->free_function(self->next, address, context); -} - -size_t GetSizeEstimateFn(const AllocatorDispatch* self, - void* address, - void* context) { - return self->next->get_size_estimate_function(self->next, address, context); -} - -bool ClaimedAddressFn(const AllocatorDispatch* self, - void* address, - void* context) { - return self->next->claimed_address_function(self->next, address, context); -} - -unsigned BatchMallocFn(const AllocatorDispatch* self, - size_t size, - void** results, - unsigned num_requested, - void* context) { - unsigned num_allocated = self->next->batch_malloc_function( - self->next, size, results, num_requested, context); - - for (unsigned i = 0; i < num_allocated; ++i) { - PoissonAllocationSampler::RecordAlloc( - results[i], size, AllocationSubsystem::kAllocatorShim, nullptr); - } - - return num_allocated; -} - -void BatchFreeFn(const AllocatorDispatch* self, - void** to_be_freed, - unsigned num_to_be_freed, - void* context) { - for (unsigned i = 0; i < num_to_be_freed; ++i) { - PoissonAllocationSampler::RecordFree(to_be_freed[i]); - } - - self->next->batch_free_function(self->next, to_be_freed, num_to_be_freed, - context); -} - -void FreeDefiniteSizeFn(const AllocatorDispatch* self, - void* address, - size_t size, - void* context) { - PoissonAllocationSampler::RecordFree(address); - self->next->free_definite_size_function(self->next, address, size, context); -} - -void TryFreeDefaultFn(const AllocatorDispatch* self, - void* address, - void* context) { - PoissonAllocationSampler::RecordFree(address); - self->next->try_free_default_function(self->next, address, context); -} - -static void* AlignedMallocFn(const AllocatorDispatch* self, - size_t size, - size_t alignment, - void* context) { - void* address = - self->next->aligned_malloc_function(self->next, size, alignment, context); - - PoissonAllocationSampler::RecordAlloc( - address, size, AllocationSubsystem::kAllocatorShim, nullptr); - - return address; -} - -static void* AlignedReallocFn(const AllocatorDispatch* self, - void* address, - size_t size, - size_t alignment, - void* context) { - // Note: size == 0 actually performs free. - PoissonAllocationSampler::RecordFree(address); - address = self->next->aligned_realloc_function(self->next, address, size, - alignment, context); - - PoissonAllocationSampler::RecordAlloc( - address, size, AllocationSubsystem::kAllocatorShim, nullptr); - - return address; -} - -static void AlignedFreeFn(const AllocatorDispatch* self, - void* address, - void* context) { - PoissonAllocationSampler::RecordFree(address); - self->next->aligned_free_function(self->next, address, context); -} - -AllocatorDispatch g_allocator_dispatch = {&AllocFn, - &AllocUncheckedFn, - &AllocZeroInitializedFn, - &AllocAlignedFn, - &ReallocFn, - &FreeFn, - &GetSizeEstimateFn, - &ClaimedAddressFn, - &BatchMallocFn, - &BatchFreeFn, - &FreeDefiniteSizeFn, - &TryFreeDefaultFn, - &AlignedMallocFn, - &AlignedReallocFn, - &AlignedFreeFn, - nullptr}; - -} // namespace -} // namespace base::allocator::dispatcher::allocator_shim_details -#endif // BUILDFLAG(USE_ALLOCATOR_SHIM) - -#if BUILDFLAG(USE_PARTITION_ALLOC) -namespace base::allocator::dispatcher::partition_allocator_details { -namespace { - -void PartitionAllocHook(void* address, size_t size, const char* type) { - PoissonAllocationSampler::RecordAlloc( - address, size, AllocationSubsystem::kPartitionAllocator, type); -} - -void PartitionFreeHook(void* address) { - PoissonAllocationSampler::RecordFree(address); -} - -} // namespace -} // namespace base::allocator::dispatcher::partition_allocator_details -#endif // BUILDFLAG(USE_PARTITION_ALLOC) -#endif // !BUILDFLAG(USE_ALLOCATION_EVENT_DISPATCHER) - -namespace base::allocator::dispatcher { - -#if !BUILDFLAG(USE_ALLOCATION_EVENT_DISPATCHER) -void InstallStandardAllocatorHooks() { -#if BUILDFLAG(USE_ALLOCATOR_SHIM) - allocator_shim::InsertAllocatorDispatch( - &allocator_shim_details::g_allocator_dispatch); -#else - // If the allocator shim isn't available, then we don't install any hooks. - // There's no point in printing an error message, since this can regularly - // happen for tests. -#endif // BUILDFLAG(USE_ALLOCATOR_SHIM) - -#if BUILDFLAG(USE_PARTITION_ALLOC) - partition_alloc::PartitionAllocHooks::SetObserverHooks( - &partition_allocator_details::PartitionAllocHook, - &partition_allocator_details::PartitionFreeHook); -#endif // BUILDFLAG(USE_PARTITION_ALLOC) -} -#endif // !BUILDFLAG(USE_ALLOCATION_EVENT_DISPATCHER) - -} // namespace base::allocator::dispatcher
diff --git a/base/allocator/dispatcher/standard_hooks.h b/base/allocator/dispatcher/standard_hooks.h deleted file mode 100644 index a933fa4..0000000 --- a/base/allocator/dispatcher/standard_hooks.h +++ /dev/null
@@ -1,25 +0,0 @@ -// 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. - -#ifndef BASE_ALLOCATOR_DISPATCHER_STANDARD_HOOKS_H_ -#define BASE_ALLOCATOR_DISPATCHER_STANDARD_HOOKS_H_ - -// This file and its cc file contain the standard allocation hooks and auxiliary -// functions. These are intended to be replaced by the new dispatcher mechanism -// in /base/allocator/dispatcher. - -#include "base/allocator/buildflags.h" -#include "base/base_export.h" - -namespace base::allocator::dispatcher { - -#if !BUILDFLAG(USE_ALLOCATION_EVENT_DISPATCHER) -// Install the standard allocation hooks which forward allocation events to the -// PoissonAllocationSampler. -void BASE_EXPORT InstallStandardAllocatorHooks(); -#endif - -} // namespace base::allocator::dispatcher - -#endif // BASE_ALLOCATOR_DISPATCHER_STANDARD_HOOKS_H_
diff --git a/base/allocator/dispatcher/subsystem.h b/base/allocator/dispatcher/subsystem.h deleted file mode 100644 index 163961e..0000000 --- a/base/allocator/dispatcher/subsystem.h +++ /dev/null
@@ -1,27 +0,0 @@ -// 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. - -#ifndef BASE_ALLOCATOR_DISPATCHER_SUBSYSTEM_H_ -#define BASE_ALLOCATOR_DISPATCHER_SUBSYSTEM_H_ - -namespace base::allocator::dispatcher { - -// Identifiers for the memory subsystem handling the allocation. Some observers -// require more detailed information on who is performing the allocation, i.e. -// SamplingHeapProfiler. -enum class AllocationSubsystem { - // Allocation is handled by PartitionAllocator. - kPartitionAllocator = 1, - // Allocation is handled by AllocatorShims. - kAllocatorShim = 2, - // Represents a simulated allocation event during testing and is used to - // filter out these allocations from real ones. - // - // Included for backward compatibility, this value becomes obsolete once the - // old allocation hooks are removed from PoissonAllocationSampler. - kManualForTesting = 3, -}; -} // namespace base::allocator::dispatcher - -#endif // BASE_ALLOCATOR_DISPATCHER_SUBSYSTEM_H_ \ No newline at end of file
diff --git a/base/allocator/dispatcher/testing/dispatcher_test.cc b/base/allocator/dispatcher/testing/dispatcher_test.cc deleted file mode 100644 index 9efa5c5..0000000 --- a/base/allocator/dispatcher/testing/dispatcher_test.cc +++ /dev/null
@@ -1,16 +0,0 @@ -// 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. - -#include "base/allocator/dispatcher/testing/dispatcher_test.h" -#include "base/allocator/dispatcher/reentry_guard.h" - -namespace base::allocator::dispatcher::testing { - -DispatcherTest::DispatcherTest() { - base::allocator::dispatcher::ReentryGuard::InitTLSSlot(); -} - -DispatcherTest::~DispatcherTest() = default; - -} // namespace base::allocator::dispatcher::testing \ No newline at end of file
diff --git a/base/allocator/dispatcher/testing/dispatcher_test.h b/base/allocator/dispatcher/testing/dispatcher_test.h deleted file mode 100644 index 21fbd1a..0000000 --- a/base/allocator/dispatcher/testing/dispatcher_test.h +++ /dev/null
@@ -1,27 +0,0 @@ -// 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. - -#ifndef BASE_ALLOCATOR_DISPATCHER_TESTING_DISPATCHER_TEST_H_ -#define BASE_ALLOCATOR_DISPATCHER_TESTING_DISPATCHER_TEST_H_ - -#include "testing/gtest/include/gtest/gtest.h" - -namespace base::allocator::dispatcher::testing { - -// DispatcherTest provides some common initialization which most of the -// unittests of the dispatcher require. DispatcherTest should not be used -// directly. Instead, derive your test fixture from it. -struct DispatcherTest : public ::testing::Test { - // Perform some commonly required initialization, at them moment - // - Initialize the TLS slot for the ReentryGuard - DispatcherTest(); - - protected: - // Protected d'tor only to prevent direct usage of this class. - ~DispatcherTest() override; -}; - -} // namespace base::allocator::dispatcher::testing - -#endif // BASE_ALLOCATOR_DISPATCHER_TESTING_DISPATCHER_TEST_H_ \ No newline at end of file
diff --git a/base/allocator/dispatcher/testing/observer_mock.h b/base/allocator/dispatcher/testing/observer_mock.h deleted file mode 100644 index b016aed..0000000 --- a/base/allocator/dispatcher/testing/observer_mock.h +++ /dev/null
@@ -1,32 +0,0 @@ -// 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. - -#ifndef BASE_ALLOCATOR_DISPATCHER_TESTING_OBSERVER_MOCK_H_ -#define BASE_ALLOCATOR_DISPATCHER_TESTING_OBSERVER_MOCK_H_ - -#include "base/allocator/dispatcher/subsystem.h" -#include "testing/gmock/include/gmock/gmock.h" - -#include <cstddef> - -namespace base::allocator::dispatcher::testing { - -// ObserverMock is a small mock class based on GoogleMock. -// It complies to the interface enforced by the dispatcher. The template -// parameter serves only to create distinct types of observers if required. -template <typename T = void> -struct ObserverMock { - MOCK_METHOD(void, - OnAllocation, - (void* address, - size_t size, - AllocationSubsystem sub_system, - const char* type_name), - ()); - MOCK_METHOD(void, OnFree, (void* address), ()); -}; - -} // namespace base::allocator::dispatcher::testing - -#endif // BASE_ALLOCATOR_DISPATCHER_TESTING_OBSERVER_MOCK_H_ \ No newline at end of file
diff --git a/base/allocator/dispatcher/testing/tools.h b/base/allocator/dispatcher/testing/tools.h deleted file mode 100644 index 4eb6fcd..0000000 --- a/base/allocator/dispatcher/testing/tools.h +++ /dev/null
@@ -1,50 +0,0 @@ -// 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. - -#ifndef BASE_ALLOCATOR_DISPATCHER_TESTING_TOOLS_H_ -#define BASE_ALLOCATOR_DISPATCHER_TESTING_TOOLS_H_ - -#include <array> -#include <tuple> -#include <utility> - -namespace base::allocator::dispatcher::testing { - -namespace internal { -template <size_t Size, typename Type, typename... AppendedTypes> -struct DefineTupleFromSingleType { - using type = typename DefineTupleFromSingleType<Size - 1, - Type, - AppendedTypes..., - Type>::type; -}; - -template <typename Type, typename... AppendedTypes> -struct DefineTupleFromSingleType<0, Type, AppendedTypes...> { - using type = std::tuple<AppendedTypes...>; -}; - -} // namespace internal - -template <size_t Size, typename Type> -struct DefineTupleFromSingleType { - using type = typename internal::DefineTupleFromSingleType<Size, Type>::type; -}; - -template <typename Type, size_t Size, size_t... Indices> -typename internal::DefineTupleFromSingleType<Size, Type*>::type -CreateTupleOfPointers(std::array<Type, Size>& items, - std::index_sequence<Indices...>) { - return std::make_tuple((&items[Indices])...); -} - -template <typename Type, size_t Size> -typename internal::DefineTupleFromSingleType<Size, Type*>::type -CreateTupleOfPointers(std::array<Type, Size>& items) { - return CreateTupleOfPointers(items, std::make_index_sequence<Size>{}); -} - -} // namespace base::allocator::dispatcher::testing - -#endif
diff --git a/base/allocator/dispatcher/tls.cc b/base/allocator/dispatcher/tls.cc deleted file mode 100644 index 6524810..0000000 --- a/base/allocator/dispatcher/tls.cc +++ /dev/null
@@ -1,100 +0,0 @@ -// 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. - -#include "base/allocator/dispatcher/tls.h" - -#if USE_LOCAL_TLS_EMULATION() - -#include "base/check.h" -#include "base/dcheck_is_on.h" -#include "base/immediate_crash.h" -#include "build/build_config.h" - -#include <sys/mman.h> - -#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) -#include <sys/prctl.h> -#endif - -namespace base::allocator::dispatcher::internal { - -void* MMapAllocator::AllocateMemory(size_t size_in_bytes) { - void* const mmap_res = mmap(nullptr, size_in_bytes, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); -#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) -#if defined(PR_SET_VMA) && defined(PR_SET_VMA_ANON_NAME) - if (mmap_res != MAP_FAILED) { - // Allow the anonymous memory region allocated by mmap(MAP_ANONYMOUS) to - // be identified in /proc/$PID/smaps. This helps improve visibility into - // Chromium's memory usage on Android. - prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, mmap_res, size_in_bytes, - "tls-mmap-allocator"); - } -#endif -#endif - - return (mmap_res != MAP_FAILED) ? mmap_res : nullptr; -} - -bool MMapAllocator::FreeMemoryForTesting(void* pointer_to_allocated, - size_t size_in_bytes) { - auto const munmap_res = munmap(pointer_to_allocated, size_in_bytes); - return (munmap_res == 0); -} - -bool PThreadTLSSystem::Setup( - OnThreadTerminationFunction thread_termination_function) { -#if DCHECK_IS_ON() - // Initialize must happen outside of the allocation path. Therefore, it is - // secure to verify with DCHECK. - DCHECK(!initialized_.exchange(true, std::memory_order_acq_rel)); -#endif - - auto const key_create_res = - pthread_key_create(&data_access_key_, thread_termination_function); - - // On some platforms creating a new pthread-key requires an allocation when a - // given number of keys has been created. I.e. in glibc this limit is denoted - // by PTHREAD_KEY_2NDLEVEL_SIZE. However, this value is neither present on all - // systems nor accessible from here. Hence, we do not do any checks here. - // However, we strongly recommend to setup the TLS system as early as possible - // to avoid exceeding this limit. - - return (0 == key_create_res); -} - -bool PThreadTLSSystem::TearDownForTesting() { -#if DCHECK_IS_ON() - // TearDownForTesting must happen outside of the allocation path. Therefore, - // it is secure to verify with DCHECK. - DCHECK(initialized_.exchange(false, std::memory_order_acq_rel)); -#endif - - auto const key_delete_res = pthread_key_delete(data_access_key_); - return (0 == key_delete_res); -} - -void* PThreadTLSSystem::GetThreadSpecificData() { -#if DCHECK_IS_ON() - if (!initialized_.load(std::memory_order_acquire)) { - return nullptr; - } -#endif - - return pthread_getspecific(data_access_key_); -} - -bool PThreadTLSSystem::SetThreadSpecificData(void* data) { -#if DCHECK_IS_ON() - if (!initialized_.load(std::memory_order_acquire)) { - return false; - } -#endif - - return (0 == pthread_setspecific(data_access_key_, data)); -} - -} // namespace base::allocator::dispatcher::internal - -#endif // USE_LOCAL_TLS_EMULATION()
diff --git a/base/allocator/dispatcher/tls.h b/base/allocator/dispatcher/tls.h deleted file mode 100644 index 9d49187..0000000 --- a/base/allocator/dispatcher/tls.h +++ /dev/null
@@ -1,432 +0,0 @@ -// 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. - -#ifndef BASE_ALLOCATOR_DISPATCHER_TLS_H_ -#define BASE_ALLOCATOR_DISPATCHER_TLS_H_ - -#include "build/build_config.h" - -#if BUILDFLAG(IS_POSIX) // the current allocation mechanism (mmap) and TLS - // support (pthread) are both defined by POSIX -#define USE_LOCAL_TLS_EMULATION() true -#else -#define USE_LOCAL_TLS_EMULATION() false -#endif - -#if USE_LOCAL_TLS_EMULATION() -#include <algorithm> -#include <atomic> -#include <memory> -#include <mutex> - -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "base/base_export.h" -#include "base/check.h" -#include "base/compiler_specific.h" - -#include <pthread.h> - -#if HAS_FEATURE(thread_sanitizer) -#define DISABLE_TSAN_INSTRUMENTATION __attribute__((no_sanitize("thread"))) -#else -#define DISABLE_TSAN_INSTRUMENTATION -#endif - -namespace base::allocator::dispatcher { -namespace internal { - -// Allocate memory using POSIX' mmap and unmap functionality. The allocator -// implements the allocator interface required by ThreadLocalStorage. -struct BASE_EXPORT MMapAllocator { -// The minimum size of a memory chunk when allocating. Even for chunks with -// fewer bytes, at least AllocationChunkSize bytes are allocated. For mmap, this -// is usually the page size of the system. -// For various OS-CPU combinations, partition_alloc::PartitionPageSize() is not -// constexpr. Hence, we can not use this value but define it locally. -#if defined(PAGE_ALLOCATOR_CONSTANTS_ARE_CONSTEXPR) && \ - PAGE_ALLOCATOR_CONSTANTS_ARE_CONSTEXPR - constexpr static size_t AllocationChunkSize = - partition_alloc::PartitionPageSize(); -#elif BUILDFLAG(IS_APPLE) - constexpr static size_t AllocationChunkSize = 16384; -#elif BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64) - constexpr static size_t AllocationChunkSize = 16384; -#else - constexpr static size_t AllocationChunkSize = 4096; -#endif - - // Allocate size_in_bytes bytes of raw memory. Return nullptr if allocation - // fails. - void* AllocateMemory(size_t size_in_bytes); - // Free the raw memory pointed to by pointer_to_allocated. Returns a boolean - // value indicating if the free was successful. - bool FreeMemoryForTesting(void* pointer_to_allocated, size_t size_in_bytes); -}; - -// The allocator used by default for the thread local storage. -using DefaultAllocator = MMapAllocator; - -using OnThreadTerminationFunction = void (*)(void*); - -// The TLS system used by default for the thread local storage. It stores and -// retrieves thread specific data pointers. -struct BASE_EXPORT PThreadTLSSystem { - // Initialize the TLS system to store a data set for different threads. - // @param thread_termination_function An optional function which will be - // invoked upon termination of a thread. - bool Setup(OnThreadTerminationFunction thread_termination_function); - // Tear down the TLS system. After completing tear down, the thread - // termination function passed to Setup will not be invoked anymore. - bool TearDownForTesting(); - - // Get the pointer to the data associated to the current thread. Returns - // nullptr if the TLS system is not initialized or no data was set before. - void* GetThreadSpecificData(); - // Set the pointer to the data associated to the current thread. Return true - // if stored successfully, false otherwise. - bool SetThreadSpecificData(void* data); - - private: - pthread_key_t data_access_key_ = 0; -#if DCHECK_IS_ON() - // From POSIX standard at https://www.open-std.org/jtc1/sc22/open/n4217.pdf: - // The effect of calling pthread_getspecific() or pthread_setspecific() with a - // key value not obtained from pthread_key_create() or after key has been - // deleted with pthread_key_delete() is undefined. - // - // Unfortunately, POSIX doesn't define a special value of pthread_key_t - // indicating an invalid key which would allow us to detect accesses outside - // of initialized state. Hence, to prevent us from drifting into the evil - // realm of undefined behaviour we store whether we're somewhere between Setup - // and Teardown. - std::atomic_bool initialized_{false}; -#endif -}; - -using DefaultTLSSystem = PThreadTLSSystem; - -// In some scenarios, most notably when testing, the allocator and TLS system -// passed to |ThreadLocalStorage| are not copyable and have to be wrapped, i.e. -// using std::reference_wrapper. |dereference| is a small helper to retrieve the -// underlying value. -template <typename T> -T& dereference(T& ref) { - return ref; -} - -template <typename T> -T& dereference(std::reference_wrapper<T>& ref) { - // std::reference_wrapper requires a valid reference for construction, - // therefore, no need in checking here. - return ref.get(); -} - -// Store thread local data. The data is organized in chunks, where each chunk -// holds |ItemsPerChunk|. Each item may be free or used. -// -// When a thread requests data, the chunks are searched for a free data item, -// which is registered for this thread and marked as |used|. Further requests by -// this thread will then always return the same item. When a thread terminates, -// the item will be reset and return to the pool of free items. -// -// Upon construction, the first chunk is created. If a thread requests data and -// there is no free item available, another chunk is created. Upon destruction, -// all memory is freed. Pointers to data items become invalid! -// -// Constructor and destructor are not thread safe. -// -// @tparam PayloadType The item type to be stored. -// @tparam AllocatorType The allocator being used. An allocator must provide -// the following interface: -// void* AllocateMemory(size_t size_in_bytes); // Allocate size_in_bytes bytes -// of raw memory. -// void FreeMemory(void* pointer_to_allocated, size_t size_in_bytes); // Free -// the raw memory pointed to by pointer_to_allocated. -// Any failure in allocation or free must terminate the process. -// @tparam TLSSystemType The TLS system being used. A TLS system must provide -// the following interface: -// bool Setup(OnThreadTerminationFunction thread_termination_function); -// bool Destroy(); -// void* GetThreadSpecificData(); -// bool SetThreadSpecificData(void* data); -// @tparam AllocationChunkSize The minimum size of a memory chunk that the -// allocator can handle. We try to size the chunks so that each chunk uses this -// size to the maximum. -// @tparam IsDestructibleForTesting For testing purposes we allow the destructor -// to perform clean up upon destruction. Otherwise, using the destructor will -// result in a compilation failure. -template <typename PayloadType, - typename AllocatorType, - typename TLSSystemType, - size_t AllocationChunkSize, - bool IsDestructibleForTesting> -struct ThreadLocalStorage { - ThreadLocalStorage() : root_(AllocateAndInitializeChunk()) { Initialize(); } - - // Create a new instance of |ThreadLocalStorage| using the passed allocator - // and TLS system. This initializes the underlying TLS system and creates the - // first chunk of data. - ThreadLocalStorage(AllocatorType allocator, TLSSystemType tlsSystem) - : allocator_(std::move(allocator)), - tls_system_(std::move(tlsSystem)), - root_(AllocateAndInitializeChunk()) { - Initialize(); - } - - // Deletes an instance of |ThreadLocalStorage| and delete all the data chunks - // created. - ~ThreadLocalStorage() { - if constexpr (IsDestructibleForTesting) { - TearDownForTesting(); - } else if constexpr (!IsDestructibleForTesting) { - static_assert( - IsDestructibleForTesting, - "ThreadLocalStorage cannot be destructed outside of test code."); - } - } - - // Explicitly prevent all forms of Copy/Move construction/assignment. For an - // exact copy of ThreadLocalStorage we would need to copy the mapping of - // thread to item, which we can't do at the moment. On the other side, our - // atomic members do not support moving out of the box. - ThreadLocalStorage(const ThreadLocalStorage&) = delete; - ThreadLocalStorage(ThreadLocalStorage&& other) = delete; - ThreadLocalStorage& operator=(const ThreadLocalStorage&) = delete; - ThreadLocalStorage& operator=(ThreadLocalStorage&&) = delete; - - // Get the data item for the current thread. If no data is registered so far, - // find a free item in the chunks and register it for the current thread. - PayloadType* GetThreadLocalData() { - auto& tls_system = dereference(tls_system_); - - auto* slot = static_cast<SingleSlot*>(tls_system.GetThreadSpecificData()); - - if (UNLIKELY(slot == nullptr)) { - slot = FindAndAllocateFreeSlot(root_.load(std::memory_order_relaxed)); - - // We might be called in the course of handling a memory allocation. We do - // not use CHECK since they might allocate and cause a recursion. - RAW_CHECK(tls_system.SetThreadSpecificData(slot)); - - // Reset the content to wipe out any previous data. - Reset(slot->item); - } - - return &(slot->item); - } - - private: - // Encapsulate the payload item and some administrative data. - struct SingleSlot { - PayloadType item; -#if !defined(__cpp_lib_atomic_value_initialization) || \ - __cpp_lib_atomic_value_initialization < 201911L - std::atomic_flag is_used = ATOMIC_FLAG_INIT; -#else - std::atomic_flag is_used; -#endif - }; - - template <size_t NumberOfItems> - struct ChunkT { - SingleSlot slots[NumberOfItems]; - // Pointer to the next chunk. - std::atomic<ChunkT*> next_chunk = nullptr; - // Helper flag to ensure we create the next chunk only once in a multi - // threaded environment. - std::once_flag create_next_chunk_flag; - }; - - template <size_t LowerNumberOfItems, - size_t UpperNumberOfItems, - size_t NumberOfBytes> - static constexpr size_t CalculateEffectiveNumberOfItemsBinSearch() { - if constexpr (LowerNumberOfItems == UpperNumberOfItems) { - return LowerNumberOfItems; - } - - constexpr size_t CurrentNumberOfItems = - (UpperNumberOfItems - LowerNumberOfItems) / 2 + LowerNumberOfItems; - - if constexpr (sizeof(ChunkT<CurrentNumberOfItems>) > NumberOfBytes) { - return CalculateEffectiveNumberOfItemsBinSearch< - LowerNumberOfItems, CurrentNumberOfItems, NumberOfBytes>(); - } - - if constexpr (sizeof(ChunkT<CurrentNumberOfItems + 1>) < NumberOfBytes) { - return CalculateEffectiveNumberOfItemsBinSearch< - CurrentNumberOfItems + 1, UpperNumberOfItems, NumberOfBytes>(); - } - - return CurrentNumberOfItems; - } - - // Calculate the maximum number of items we can store in one chunk without the - // size of the chunk exceeding NumberOfBytes. To avoid things like alignment - // and packing tampering with the calculation, instead of calculating the - // correct number of items we use sizeof-operator against ChunkT to search for - // the correct size. Unfortunately, the number of recursions is limited by the - // compiler. Therefore, we use a binary search instead of a simple linear - // search. - template <size_t MinimumNumberOfItems, size_t NumberOfBytes> - static constexpr size_t CalculateEffectiveNumberOfItems() { - if constexpr (sizeof(ChunkT<MinimumNumberOfItems>) < NumberOfBytes) { - constexpr size_t LowerNumberOfItems = MinimumNumberOfItems; - constexpr size_t UpperNumberOfItems = - NumberOfBytes / sizeof(PayloadType) + 1; - return CalculateEffectiveNumberOfItemsBinSearch< - LowerNumberOfItems, UpperNumberOfItems, NumberOfBytes>(); - } - - return MinimumNumberOfItems; - } - - public: - // The minimum number of items per chunk. It should be high enough to - // accommodate most items in the root chunk whilst not wasting to much space - // on unnecessary items. - static constexpr size_t MinimumNumberOfItemsPerChunk = 75; - // The effective number of items per chunk. We use the AllocationChunkSize as - // a hint to calculate to effective number of items so we occupy one of these - // memory chunks to the maximum extent possible. - static constexpr size_t ItemsPerChunk = - CalculateEffectiveNumberOfItems<MinimumNumberOfItemsPerChunk, - AllocationChunkSize>(); - - private: - using Chunk = ChunkT<ItemsPerChunk>; - - static_assert(ItemsPerChunk >= MinimumNumberOfItemsPerChunk); - - // Mark an item's slot ready for reuse. This function is used as thread - // termination function in the TLS system. We do not destroy anything at this - // point but simply mark the slot as unused. - static void MarkSlotAsFree(void* data) { - // We always store SingleSlots in the TLS system. Therefore, we cast to - // SingleSlot and reset the is_used flag. - auto* const slot = static_cast<SingleSlot*>(data); - - // We might be called in the course of handling a memory allocation. We do - // not use CHECK since they might allocate and cause a recursion. - RAW_CHECK(slot && slot->is_used.test_and_set()); - - slot->is_used.clear(std::memory_order_relaxed); - } - - // Perform common initialization during construction of an instance. - void Initialize() { - // The constructor must be called outside of the allocation path. Therefore, - // it is secure to verify with CHECK. - - // Passing MarkSlotAsFree as thread_termination_function we ensure the - // slot/item assigned to the finished thread will be returned to the pool of - // unused items. - CHECK(dereference(tls_system_).Setup(&MarkSlotAsFree)); - } - - Chunk* AllocateAndInitializeChunk() { - void* const uninitialized_memory = - dereference(allocator_).AllocateMemory(sizeof(Chunk)); - - // We might be called in the course of handling a memory allocation. We do - // not use CHECK since they might allocate and cause a recursion. - RAW_CHECK(uninitialized_memory != nullptr); - - return new (uninitialized_memory) Chunk{}; - } - - void FreeAndDeallocateChunkForTesting(Chunk* chunk_to_erase) { - chunk_to_erase->~Chunk(); - - // FreeAndDeallocateChunkForTesting must be called outside of the allocation - // path. Therefore, it is secure to verify with CHECK. - CHECK(dereference(allocator_) - .FreeMemoryForTesting(chunk_to_erase, sizeof(Chunk))); - } - - // Find a free slot in the passed chunk, reserve it and return it to the - // caller. If no free slot can be found, head on to the next chunk. If the - // next chunk doesn't exist, create it. - SingleSlot* FindAndAllocateFreeSlot(Chunk* const chunk) { - SingleSlot* const slot = std::find_if_not( - std::begin(chunk->slots), std::end(chunk->slots), - [](SingleSlot& candidate_slot) { - return candidate_slot.is_used.test_and_set(std::memory_order_relaxed); - }); - - // So we found a slot. Happily return it to the caller. - if (slot != std::end(chunk->slots)) { - return slot; - } - - // Ok, there are no more free slots in this chunk. First, ensure the next - // chunk is valid and create one if necessary. - std::call_once(chunk->create_next_chunk_flag, [&] { - // From https://eel.is/c++draft/thread.once.callonce#3 - // - // Synchronization: For any given once_flag: all active executions occur - // in a total order; completion of an active execution synchronizes with - // the start of the next one in this total order; and the returning - // execution synchronizes with the return from all passive executions. - // - // Therefore, we do only a relaxed store here, call_once synchronizes with - // other threads. - chunk->next_chunk.store(AllocateAndInitializeChunk(), - std::memory_order_relaxed); - }); - - return FindAndAllocateFreeSlot(chunk->next_chunk); - } - - template <bool IsDestructibleForTestingP = IsDestructibleForTesting> - typename std::enable_if<IsDestructibleForTestingP>::type - TearDownForTesting() { - // The destructor must be called outside of the allocation path. Therefore, - // it is secure to verify with CHECK. - - // All accessing threads must be terminated by now. For additional security - // we tear down the TLS system first. This way we ensure that - // MarkSlotAsFree is not called anymore and we have no accesses from the - // TLS system's side. - CHECK(dereference(tls_system_).TearDownForTesting()); - - // Delete all data chunks. - for (auto* chunk = root_.load(); chunk != nullptr;) { - auto* next_chunk = chunk->next_chunk.load(); - FreeAndDeallocateChunkForTesting(chunk); - chunk = next_chunk; - } - } - - // Reset a single item to its default value. - // Since items are re-used, they may be accessed from different threads, - // causing TSan to trigger. Therefore, the reset is exempt from TSan - // instrumentation. - DISABLE_TSAN_INSTRUMENTATION void Reset(PayloadType& item) { item = {}; } - - AllocatorType allocator_; - TLSSystemType tls_system_; - std::atomic<Chunk*> const root_; -}; - -} // namespace internal - -// The ThreadLocalStorage visible to the user. This uses the internal default -// allocator and TLS system. -template <typename StorageType, - typename AllocatorType = internal::DefaultAllocator, - typename TLSSystemType = internal::DefaultTLSSystem, - size_t AllocationChunkSize = AllocatorType::AllocationChunkSize, - bool IsDestructibleForTesting = false> -using ThreadLocalStorage = - internal::ThreadLocalStorage<StorageType, - AllocatorType, - TLSSystemType, - AllocationChunkSize, - IsDestructibleForTesting>; - -} // namespace base::allocator::dispatcher - -#endif // USE_LOCAL_TLS_EMULATION() -#endif // BASE_ALLOCATOR_DISPATCHER_TLS_H_
diff --git a/base/allocator/dispatcher/tls_unittest.cc b/base/allocator/dispatcher/tls_unittest.cc deleted file mode 100644 index ccbe08a..0000000 --- a/base/allocator/dispatcher/tls_unittest.cc +++ /dev/null
@@ -1,588 +0,0 @@ -// 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. - -#include "base/allocator/dispatcher/tls.h" - -#if USE_LOCAL_TLS_EMULATION() -#include <algorithm> -#include <array> -#include <cstddef> -#include <functional> -#include <mutex> -#include <thread> -#include <unordered_set> -#include <utility> - -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using ::testing::_; -using ::testing::InSequence; -using ::testing::NiceMock; -using ::testing::NotNull; -using ::testing::Return; -using ::testing::ReturnNull; - -namespace base::allocator::dispatcher { -namespace { -struct DataToStore { - int data_int = 0; - float data_float = 0.0; - size_t data_size_t = 0; - double data_double = 0.0; -}; - -struct AllocatorMockBase { - AllocatorMockBase() { - ON_CALL(*this, AllocateMemory(_)).WillByDefault([](size_t size_in_bytes) { - return malloc(size_in_bytes); - }); - ON_CALL(*this, FreeMemoryForTesting(_, _)) - .WillByDefault([](void* pointer_to_allocated, size_t size_in_bytes) { - free(pointer_to_allocated); - return true; - }); - } - - MOCK_METHOD(void*, AllocateMemory, (size_t size_in_bytes), ()); - MOCK_METHOD(bool, - FreeMemoryForTesting, - (void* pointer_to_allocated, size_t size_in_bytes), - ()); -}; - -struct TLSSystemMockBase { - TLSSystemMockBase() { - ON_CALL(*this, Setup(_)).WillByDefault(Return(true)); - ON_CALL(*this, TearDownForTesting()).WillByDefault(Return(true)); - ON_CALL(*this, SetThreadSpecificData(_)).WillByDefault(Return(true)); - } - - MOCK_METHOD( - bool, - Setup, - (internal::OnThreadTerminationFunction thread_termination_function), - ()); - MOCK_METHOD(bool, TearDownForTesting, (), ()); - MOCK_METHOD(void*, GetThreadSpecificData, (), ()); - MOCK_METHOD(bool, SetThreadSpecificData, (void* data), ()); -}; - -using AllocatorMock = NiceMock<AllocatorMockBase>; -using TLSSystemMock = NiceMock<TLSSystemMockBase>; - -template <typename T, typename Allocator, typename TLSSystem> -ThreadLocalStorage<T, - std::reference_wrapper<Allocator>, - std::reference_wrapper<TLSSystem>, - 0, - true> -CreateThreadLocalStorage(Allocator& allocator, TLSSystem& tlsSystem) { - return {std::ref(allocator), std::ref(tlsSystem)}; -} - -template <typename T> -ThreadLocalStorage<T, - internal::DefaultAllocator, - internal::DefaultTLSSystem, - 0, - true> -CreateThreadLocalStorage() { - return {}; -} - -} // namespace - -struct BaseThreadLocalStorageTest : public ::testing::Test {}; - -TEST_F(BaseThreadLocalStorageTest, - VerifyDataIsIndependentBetweenDifferentSUTs) { - auto sut_1 = CreateThreadLocalStorage<DataToStore>(); - auto sut_2 = CreateThreadLocalStorage<DataToStore>(); - - EXPECT_NE(sut_1.GetThreadLocalData(), sut_2.GetThreadLocalData()); -} - -TEST_F(BaseThreadLocalStorageTest, VerifyDistinctEntriesForEachThread) { - auto sut = CreateThreadLocalStorage<DataToStore>(); - using TLSType = decltype(sut); - - std::array<std::thread, 2 * TLSType::ItemsPerChunk> threads; - std::mutex thread_worker_mutex; - std::condition_variable thread_counter_cv; - std::atomic_uint32_t thread_counter{0}; - std::unordered_set<void*> stored_object_addresses; - - std::mutex threads_can_finish_mutex; - std::condition_variable threads_can_finish_cv; - std::atomic_bool threads_can_finish{false}; - - for (auto& t : threads) { - t = std::thread{[&] { - { - std::lock_guard<std::mutex> lock(thread_worker_mutex); - stored_object_addresses.insert(sut.GetThreadLocalData()); - ++thread_counter; - thread_counter_cv.notify_one(); - } - - { - std::unique_lock<std::mutex> lock(threads_can_finish_mutex); - threads_can_finish_cv.wait(lock, - [&] { return threads_can_finish.load(); }); - } - }}; - } - - { - std::unique_lock<std::mutex> lock(thread_worker_mutex); - thread_counter_cv.wait( - lock, [&] { return thread_counter.load() == threads.size(); }); - } - - { - std::unique_lock<std::mutex> lock(threads_can_finish_mutex); - threads_can_finish = true; - threads_can_finish_cv.notify_all(); - } - - for (auto& t : threads) { - t.join(); - } - - EXPECT_EQ(stored_object_addresses.size(), threads.size()); -} - -TEST_F(BaseThreadLocalStorageTest, VerifyEntriesAreReusedForNewThreads) { - auto sut = CreateThreadLocalStorage<DataToStore>(); - using TLSType = decltype(sut); - - std::unordered_set<void*> stored_object_addresses; - - for (size_t thread_count = 0; thread_count < (2 * TLSType::ItemsPerChunk); - ++thread_count) { - auto thread = std::thread{ - [&] { stored_object_addresses.insert(sut.GetThreadLocalData()); }}; - - thread.join(); - } - - EXPECT_EQ(stored_object_addresses.size(), 1ul); -} - -TEST_F(BaseThreadLocalStorageTest, VerifyDataIsSameWithinEachThread) { - auto sut = CreateThreadLocalStorage<DataToStore>(); - using TLSType = decltype(sut); - - std::array<std::thread, 2 * TLSType::ItemsPerChunk> threads; - - for (auto& t : threads) { - t = std::thread{[&] { - EXPECT_EQ(sut.GetThreadLocalData(), sut.GetThreadLocalData()); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - // Check once again to verify the data doesn't change in the course of a - // thread's lifetime. - EXPECT_EQ(sut.GetThreadLocalData(), sut.GetThreadLocalData()); - }}; - } - - for (auto& t : threads) { - t.join(); - } -} - -TEST_F(BaseThreadLocalStorageTest, VerifySetupTeardownSequence) { - AllocatorMock allocator_mock; - TLSSystemMock tlsSystem_mock; - - InSequence execution_sequence; - - EXPECT_CALL(allocator_mock, AllocateMemory(_)) - .WillOnce([](size_t size_in_bytes) { return malloc(size_in_bytes); }); - EXPECT_CALL(tlsSystem_mock, Setup(NotNull())).WillOnce(Return(true)); - EXPECT_CALL(tlsSystem_mock, TearDownForTesting()).WillOnce(Return(true)); - EXPECT_CALL(allocator_mock, FreeMemoryForTesting(_, _)) - .WillOnce([](void* pointer_to_allocated, size_t size_in_bytes) { - free(pointer_to_allocated); - return true; - }); - - auto sut = - CreateThreadLocalStorage<DataToStore>(allocator_mock, tlsSystem_mock); -} - -TEST_F(BaseThreadLocalStorageTest, VerifyAllocatorIsUsed) { - AllocatorMock allocator_mock; - TLSSystemMock tlsSystem_mock; - - EXPECT_CALL(allocator_mock, AllocateMemory(_)) - .WillOnce([](size_t size_in_bytes) { return malloc(size_in_bytes); }); - - EXPECT_CALL(allocator_mock, FreeMemoryForTesting(_, _)) - .WillOnce([](void* pointer_to_allocated, size_t size_in_bytes) { - free(pointer_to_allocated); - return true; - }); - - auto sut = - CreateThreadLocalStorage<DataToStore>(allocator_mock, tlsSystem_mock); -} - -TEST_F(BaseThreadLocalStorageTest, VerifyAllocatorIsUsedForMultipleChunks) { - AllocatorMock allocator_mock; - TLSSystemMock tlsSystem_mock; - - constexpr auto number_of_chunks = 3; - - EXPECT_CALL(allocator_mock, AllocateMemory(_)) - .Times(number_of_chunks) - .WillRepeatedly( - [](size_t size_in_bytes) { return malloc(size_in_bytes); }); - - EXPECT_CALL(allocator_mock, FreeMemoryForTesting(_, _)) - .Times(number_of_chunks) - .WillRepeatedly([](void* pointer_to_allocated, size_t size_in_bytes) { - free(pointer_to_allocated); - return true; - }); - - auto sut = - CreateThreadLocalStorage<DataToStore>(allocator_mock, tlsSystem_mock); - - std::array<std::thread, - (number_of_chunks - 1) * decltype(sut)::ItemsPerChunk + 1> - threads; - std::mutex thread_worker_mutex; - std::condition_variable thread_counter_cv; - std::atomic_uint32_t thread_counter{0}; - std::unordered_set<void*> stored_object_addresses; - - std::mutex threads_can_finish_mutex; - std::condition_variable threads_can_finish_cv; - std::atomic_bool threads_can_finish{false}; - - for (auto& t : threads) { - t = std::thread{[&] { - sut.GetThreadLocalData(); - - { - std::lock_guard<std::mutex> lock(thread_worker_mutex); - ++thread_counter; - thread_counter_cv.notify_one(); - } - - { - std::unique_lock<std::mutex> lock(threads_can_finish_mutex); - threads_can_finish_cv.wait(lock, - [&] { return threads_can_finish.load(); }); - } - }}; - } - - { - std::unique_lock<std::mutex> lock(thread_worker_mutex); - thread_counter_cv.wait( - lock, [&] { return thread_counter.load() == threads.size(); }); - } - - { - std::unique_lock<std::mutex> lock(threads_can_finish_mutex); - threads_can_finish = true; - threads_can_finish_cv.notify_all(); - } - - for (auto& t : threads) { - t.join(); - } -} - -TEST_F(BaseThreadLocalStorageTest, VerifyTLSSystemIsUsed) { - AllocatorMock allocator_mock; - TLSSystemMock tlsSystem_mock; - - InSequence execution_sequence; - - EXPECT_CALL(tlsSystem_mock, Setup(NotNull())).WillOnce(Return(true)); - EXPECT_CALL(tlsSystem_mock, GetThreadSpecificData()) - .WillOnce(Return(nullptr)); - EXPECT_CALL(tlsSystem_mock, SetThreadSpecificData(NotNull())); - EXPECT_CALL(tlsSystem_mock, TearDownForTesting()) - .Times(1) - .WillOnce(Return(true)); - - auto sut = - CreateThreadLocalStorage<DataToStore>(allocator_mock, tlsSystem_mock); - - sut.GetThreadLocalData(); -} - -#if defined(GTEST_HAS_DEATH_TEST) -struct BaseThreadLocalStorageDeathTest : public ::testing::Test {}; - -TEST_F(BaseThreadLocalStorageDeathTest, VerifyDeathIfAllocationFails) { - auto f = [] { - AllocatorMock allocator_mock; - TLSSystemMock tlsSystem_mock; - - // Setup all expectations here. If we're setting them up in the parent - // process, they will fail because the parent doesn't execute any test. - EXPECT_CALL(allocator_mock, AllocateMemory(_)).WillOnce(ReturnNull()); - - CreateThreadLocalStorage<DataToStore>(allocator_mock, tlsSystem_mock); - }; - - EXPECT_DEATH(f(), ""); -} - -TEST_F(BaseThreadLocalStorageDeathTest, VerifyDeathIfFreeFails) { - auto f = [] { - AllocatorMock allocator_mock; - TLSSystemMock tlsSystem_mock; - - // Setup all expectations here. If we're setting them up in the parent - // process, they will fail because the parent doesn't execute any test. - EXPECT_CALL(allocator_mock, FreeMemoryForTesting(_, _)) - .WillOnce([](void* allocated_memory, size_t size_in_bytes) { - free(allocated_memory); - return false; - }); - - CreateThreadLocalStorage<DataToStore>(allocator_mock, tlsSystem_mock); - }; - - EXPECT_DEATH(f(), ""); -} - -TEST_F(BaseThreadLocalStorageDeathTest, VerifyDeathIfTLSSetupFails) { - auto f = [] { - AllocatorMock allocator_mock; - TLSSystemMock tlsSystem_mock; - - // Setup all expectations here. If we're setting them up in the parent - // process, they will fail because the parent doesn't execute any test. - EXPECT_CALL(tlsSystem_mock, Setup(_)).WillOnce(Return(false)); - EXPECT_CALL(tlsSystem_mock, GetThreadSpecificData()).Times(0); - EXPECT_CALL(tlsSystem_mock, SetThreadSpecificData(_)).Times(0); - EXPECT_CALL(tlsSystem_mock, TearDownForTesting()).Times(0); - - CreateThreadLocalStorage<DataToStore>(allocator_mock, tlsSystem_mock); - }; - - EXPECT_DEATH(f(), ""); -} - -TEST_F(BaseThreadLocalStorageDeathTest, VerifyDeathIfStoringTLSDataFails) { - auto f = [] { - AllocatorMock allocator_mock; - TLSSystemMock tlsSystem_mock; - - // Setup all expectations here. If we're setting them up in the parent - // process, they will fail because the parent doesn't execute any test. - EXPECT_CALL(tlsSystem_mock, SetThreadSpecificData(_)) - .Times(1) - .WillOnce(Return(false)); - EXPECT_CALL(tlsSystem_mock, TearDownForTesting()).Times(0); - - CreateThreadLocalStorage<DataToStore>(allocator_mock, tlsSystem_mock) - .GetThreadLocalData(); - }; - - EXPECT_DEATH(f(), ""); -} - -TEST_F(BaseThreadLocalStorageDeathTest, VerifyDeathIfTLSTeardownFails) { - auto f = [] { - AllocatorMock allocator_mock; - TLSSystemMock tlsSystem_mock; - - // Setup all expectations here. If we're setting them up in the parent - // process, they will fail because the parent doesn't execute any test. - EXPECT_CALL(tlsSystem_mock, Setup(_)).WillOnce(Return(true)); - EXPECT_CALL(tlsSystem_mock, TearDownForTesting()).WillOnce(Return(false)); - - CreateThreadLocalStorage<DataToStore>(allocator_mock, tlsSystem_mock); - }; - - EXPECT_DEATH(f(), ""); -} -#endif // GTEST_HAS_DEATH_TEST - -struct BasePThreadTLSSystemTest : public ::testing::Test { - void SetUp() override { thread_termination_counter = 0; } - - protected: - static void ThreadTerminationFunction(void*) { ++thread_termination_counter; } - - static std::atomic<size_t> thread_termination_counter; -}; - -std::atomic<size_t> BasePThreadTLSSystemTest::thread_termination_counter{0}; - -TEST_F(BasePThreadTLSSystemTest, VerifySetupNTeardownSequence) { - internal::PThreadTLSSystem sut; - - for (size_t idx = 0; idx < 5; ++idx) { - EXPECT_TRUE(sut.Setup(nullptr)); - EXPECT_TRUE(sut.TearDownForTesting()); - } -} - -TEST_F(BasePThreadTLSSystemTest, VerifyThreadTerminationFunctionIsCalled) { - std::array<std::thread, 10> threads; - - internal::PThreadTLSSystem sut; - sut.Setup(&ThreadTerminationFunction); - - for (auto& t : threads) { - t = std::thread{[&] { - int x = 0; - ASSERT_TRUE(sut.SetThreadSpecificData(&x)); - }}; - } - - for (auto& t : threads) { - t.join(); - } - - sut.TearDownForTesting(); - - EXPECT_EQ(threads.size(), thread_termination_counter); -} - -TEST_F(BasePThreadTLSSystemTest, VerifyGetWithoutSetReturnsNull) { - internal::PThreadTLSSystem sut; - sut.Setup(nullptr); - - EXPECT_EQ(nullptr, sut.GetThreadSpecificData()); - - sut.TearDownForTesting(); -} - -TEST_F(BasePThreadTLSSystemTest, VerifyGetAfterTeardownReturnsNull) { - internal::PThreadTLSSystem sut; - sut.Setup(nullptr); - sut.SetThreadSpecificData(this); - sut.TearDownForTesting(); - - EXPECT_EQ(sut.GetThreadSpecificData(), nullptr); -} - -TEST_F(BasePThreadTLSSystemTest, VerifyGetAfterTeardownReturnsNullThreaded) { - std::array<std::thread, 50> threads; - - std::mutex thread_worker_mutex; - std::condition_variable thread_counter_cv; - std::atomic_uint32_t thread_counter{0}; - - std::mutex threads_can_finish_mutex; - std::condition_variable threads_can_finish_cv; - std::atomic_bool threads_can_finish{false}; - - internal::PThreadTLSSystem sut; - ASSERT_TRUE(sut.Setup(nullptr)); - - for (auto& t : threads) { - t = std::thread{[&] { - // Set some thread specific data. At this stage retrieving the data must - // return the pointer that was originally set. - int x = 0; - ASSERT_TRUE(sut.SetThreadSpecificData(&x)); - ASSERT_EQ(sut.GetThreadSpecificData(), &x); - - // Notify the main thread that one more test thread has started. - { - std::lock_guard<std::mutex> lock(thread_worker_mutex); - ++thread_counter; - thread_counter_cv.notify_one(); - } - - // Wait for the main thread to notify about teardown of the sut. - { - std::unique_lock<std::mutex> lock(threads_can_finish_mutex); - threads_can_finish_cv.wait(lock, - [&] { return threads_can_finish.load(); }); - } - - // After teardown, thread local data must be nullptr for all threads. - EXPECT_EQ(sut.GetThreadSpecificData(), nullptr); - }}; - } - - // Wait for notification from threads that they started and passed the initial - // check. - { - std::unique_lock<std::mutex> lock(thread_worker_mutex); - thread_counter_cv.wait( - lock, [&] { return thread_counter.load() == threads.size(); }); - } - - ASSERT_TRUE(sut.TearDownForTesting()); - - // Notify all threads that the subject under test was torn down and they can - // proceed. - { - std::unique_lock<std::mutex> lock(threads_can_finish_mutex); - threads_can_finish = true; - threads_can_finish_cv.notify_all(); - } - - for (auto& t : threads) { - t.join(); - } -} - -TEST_F(BasePThreadTLSSystemTest, VerifyGetSetSequence) { - std::array<std::thread, 50> threads; - - internal::PThreadTLSSystem sut; - sut.Setup(nullptr); - - for (auto& t : threads) { - t = std::thread{[&] { - int x = 0; - EXPECT_TRUE(sut.SetThreadSpecificData(&x)); - EXPECT_EQ(&x, sut.GetThreadSpecificData()); - }}; - } - - for (auto& t : threads) { - t.join(); - } - - sut.TearDownForTesting(); -} - -#if DCHECK_IS_ON() -TEST_F(BasePThreadTLSSystemTest, VerifyGetWithoutSetupReturnsNull) { - internal::PThreadTLSSystem sut; - - EXPECT_EQ(sut.GetThreadSpecificData(), nullptr); -} - -TEST_F(BasePThreadTLSSystemTest, VerifyStoreWithoutSetupFails) { - internal::PThreadTLSSystem sut; - - EXPECT_FALSE(sut.SetThreadSpecificData(this)); -} -#endif - -#if defined(GTEST_HAS_DEATH_TEST) && DCHECK_IS_ON() -struct BasePThreadTLSSystemDeathTest : public ::testing::Test {}; - -TEST_F(BasePThreadTLSSystemDeathTest, VerifyDeathIfSetupTwice) { - internal::PThreadTLSSystem sut; - - EXPECT_TRUE(sut.Setup(nullptr)); - EXPECT_DEATH(sut.Setup(nullptr), ""); -} - -TEST_F(BasePThreadTLSSystemDeathTest, VerifyDeathIfTearDownWithoutSetup) { - internal::PThreadTLSSystem sut; - - EXPECT_DEATH(sut.TearDownForTesting(), ""); -} -#endif -} // namespace base::allocator::dispatcher - -#endif // USE_LOCAL_TLS_EMULATION() \ No newline at end of file
diff --git a/base/allocator/early_zone_registration_mac.cc b/base/allocator/early_zone_registration_mac.cc deleted file mode 100644 index a4eb1a2..0000000 --- a/base/allocator/early_zone_registration_mac.cc +++ /dev/null
@@ -1,262 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/early_zone_registration_mac.h" - -#include <mach/mach.h> -#include <malloc/malloc.h> - -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" - -// BASE_EXPORT tends to be defined as soon as anything from //base is included. -#if defined(BASE_EXPORT) -#error "This file cannot depend on //base" -#endif - -namespace partition_alloc { - -#if !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - -void EarlyMallocZoneRegistration() {} -void AllowDoublePartitionAllocZoneRegistration() {} - -#else - -extern "C" { -// abort_report_np() records the message in a special section that both the -// system CrashReporter and Crashpad collect in crash reports. See also in -// chrome_exe_main_mac.cc. -void abort_report_np(const char* fmt, ...); -} - -namespace { - -malloc_zone_t* GetDefaultMallocZone() { - // malloc_default_zone() does not return... the default zone, but the - // initial one. The default one is the first element of the default zone - // array. - unsigned int zone_count = 0; - vm_address_t* zones = nullptr; - kern_return_t result = - malloc_get_all_zones(mach_task_self(), nullptr, &zones, &zone_count); - if (result != KERN_SUCCESS) - abort_report_np("Cannot enumerate malloc() zones"); - return reinterpret_cast<malloc_zone_t*>(zones[0]); -} - -} // namespace - -void EarlyMallocZoneRegistration() { - // Must have static storage duration, as raw pointers are passed to - // libsystem_malloc. - static malloc_zone_t g_delegating_zone; - static malloc_introspection_t g_delegating_zone_introspect; - static malloc_zone_t* g_default_zone; - - // Make sure that the default zone is instantiated. - malloc_zone_t* purgeable_zone = malloc_default_purgeable_zone(); - - g_default_zone = GetDefaultMallocZone(); - - // The delegating zone: - // - Forwards all allocations to the existing default zone - // - Does *not* claim to own any memory, meaning that it will always be - // skipped in free() in libsystem_malloc.dylib. - // - // This is a temporary zone, until it gets replaced by PartitionAlloc, inside - // the main library. Since the main library depends on many external - // libraries, we cannot install PartitionAlloc as the default zone without - // concurrency issues. - // - // Instead, what we do is here, while the process is single-threaded: - // - Register the delegating zone as the default one. - // - Set the original (libsystem_malloc's) one as the second zone - // - // Later, when PartitionAlloc initializes, we replace the default (delegating) - // zone with ours. The end state is: - // 1. PartitionAlloc zone - // 2. libsystem_malloc zone - - // Set up of the delegating zone. Note that it doesn't just forward calls to - // the default zone. This is because the system zone's malloc_zone_t pointer - // actually points to a larger struct, containing allocator metadata. So if we - // pass as the first parameter the "simple" delegating zone pointer, then we - // immediately crash inside the system zone functions. So we need to replace - // the zone pointer as well. - // - // Calls fall into 4 categories: - // - Allocation calls: forwarded to the real system zone - // - "Is this pointer yours" calls: always answer no - // - free(): Should never be called, but is in practice, see comments below. - // - Diagnostics and debugging: these are typically called for every - // zone. They are no-ops for us, as we don't want to double-count, or lock - // the data structures of the real zone twice. - - // Allocation: Forward to the real zone. - g_delegating_zone.malloc = [](malloc_zone_t* zone, size_t size) { - return g_default_zone->malloc(g_default_zone, size); - }; - g_delegating_zone.calloc = [](malloc_zone_t* zone, size_t num_items, - size_t size) { - return g_default_zone->calloc(g_default_zone, num_items, size); - }; - g_delegating_zone.valloc = [](malloc_zone_t* zone, size_t size) { - return g_default_zone->valloc(g_default_zone, size); - }; - g_delegating_zone.realloc = [](malloc_zone_t* zone, void* ptr, size_t size) { - return g_default_zone->realloc(g_default_zone, ptr, size); - }; - g_delegating_zone.batch_malloc = [](malloc_zone_t* zone, size_t size, - void** results, unsigned num_requested) { - return g_default_zone->batch_malloc(g_default_zone, size, results, - num_requested); - }; - g_delegating_zone.memalign = [](malloc_zone_t* zone, size_t alignment, - size_t size) { - return g_default_zone->memalign(g_default_zone, alignment, size); - }; - - // Does ptr belong to this zone? Return value is != 0 if so. - g_delegating_zone.size = [](malloc_zone_t* zone, const void* ptr) -> size_t { - return 0; - }; - - // Free functions. - // The normal path for freeing memory is: - // 1. Try all zones in order, call zone->size(ptr) - // 2. If zone->size(ptr) != 0, call zone->free(ptr) (or free_definite_size) - // 3. If no zone matches, crash. - // - // Since this zone always returns 0 in size() (see above), then zone->free() - // should never be called. Unfortunately, this is not the case, as some places - // in CoreFoundation call malloc_zone_free(zone, ptr) directly. So rather than - // crashing, forward the call. It's the caller's responsibility to use the - // same zone for free() as for the allocation (this is in the contract of - // malloc_zone_free()). - // - // However, note that the sequence of calls size() -> free() is not possible - // for this zone, as size() always returns 0. - g_delegating_zone.free = [](malloc_zone_t* zone, void* ptr) { - return g_default_zone->free(g_default_zone, ptr); - }; - g_delegating_zone.free_definite_size = [](malloc_zone_t* zone, void* ptr, - size_t size) { - return g_default_zone->free_definite_size(g_default_zone, ptr, size); - }; - g_delegating_zone.batch_free = [](malloc_zone_t* zone, void** to_be_freed, - unsigned num_to_be_freed) { - return g_default_zone->batch_free(g_default_zone, to_be_freed, - num_to_be_freed); - }; -#if PA_TRY_FREE_DEFAULT_IS_AVAILABLE - g_delegating_zone.try_free_default = [](malloc_zone_t* zone, void* ptr) { - return g_default_zone->try_free_default(g_default_zone, ptr); - }; -#endif - - // Diagnostics and debugging. - // - // Do nothing to reduce memory footprint, the real - // zone will do it. - g_delegating_zone.pressure_relief = [](malloc_zone_t* zone, - size_t goal) -> size_t { return 0; }; - - // Introspection calls are not all optional, for instance locking and - // unlocking before/after fork() is not optional. - // - // Nothing to enumerate. - g_delegating_zone_introspect.enumerator = - [](task_t task, void*, unsigned type_mask, vm_address_t zone_address, - memory_reader_t reader, - vm_range_recorder_t recorder) -> kern_return_t { - return KERN_SUCCESS; - }; - // Need to provide a real implementation, it is used for e.g. array sizing. - g_delegating_zone_introspect.good_size = [](malloc_zone_t* zone, - size_t size) { - return g_default_zone->introspect->good_size(g_default_zone, size); - }; - // Nothing to do. - g_delegating_zone_introspect.check = [](malloc_zone_t* zone) -> boolean_t { - return true; - }; - g_delegating_zone_introspect.print = [](malloc_zone_t* zone, - boolean_t verbose) {}; - g_delegating_zone_introspect.log = [](malloc_zone_t*, void*) {}; - // Do not forward the lock / unlock calls. Since the default zone is still - // there, we should not lock here, as it would lock the zone twice (all - // zones are locked before fork().). Rather, do nothing, since this fake - // zone does not need any locking. - g_delegating_zone_introspect.force_lock = [](malloc_zone_t* zone) {}; - g_delegating_zone_introspect.force_unlock = [](malloc_zone_t* zone) {}; - g_delegating_zone_introspect.reinit_lock = [](malloc_zone_t* zone) {}; - // No stats. - g_delegating_zone_introspect.statistics = [](malloc_zone_t* zone, - malloc_statistics_t* stats) {}; - // We are not locked. - g_delegating_zone_introspect.zone_locked = - [](malloc_zone_t* zone) -> boolean_t { return false; }; - // Don't support discharge checking. - g_delegating_zone_introspect.enable_discharge_checking = - [](malloc_zone_t* zone) -> boolean_t { return false; }; - g_delegating_zone_introspect.disable_discharge_checking = - [](malloc_zone_t* zone) {}; - g_delegating_zone_introspect.discharge = [](malloc_zone_t* zone, - void* memory) {}; - - // Could use something lower to support fewer functions, but this is - // consistent with the real zone installed by PartitionAlloc. - g_delegating_zone.version = kZoneVersion; - g_delegating_zone.introspect = &g_delegating_zone_introspect; - // This name is used in PartitionAlloc's initialization to determine whether - // it should replace the delegating zone. - g_delegating_zone.zone_name = kDelegatingZoneName; - - // Register puts the new zone at the end, unregister swaps the new zone with - // the last one. - // The zone array is, after these lines, in order: - // 1. |g_default_zone|...|g_delegating_zone| - // 2. |g_delegating_zone|...| (no more default) - // 3. |g_delegating_zone|...|g_default_zone| - malloc_zone_register(&g_delegating_zone); - malloc_zone_unregister(g_default_zone); - malloc_zone_register(g_default_zone); - - // Make sure that the purgeable zone is after the default one. - // Will make g_default_zone take the purgeable zone spot - malloc_zone_unregister(purgeable_zone); - // Add back the purgeable zone as the last one. - malloc_zone_register(purgeable_zone); - - // Final configuration: - // |g_delegating_zone|...|g_default_zone|purgeable_zone| - - // Sanity check. - if (GetDefaultMallocZone() != &g_delegating_zone) - abort_report_np("Failed to install the delegating zone as default."); -} - -void AllowDoublePartitionAllocZoneRegistration() { - unsigned int zone_count = 0; - vm_address_t* zones = nullptr; - kern_return_t result = - malloc_get_all_zones(mach_task_self(), nullptr, &zones, &zone_count); - if (result != KERN_SUCCESS) - abort_report_np("Cannot enumerate malloc() zones"); - - // If PartitionAlloc is one of the zones, *change* its name so that - // registration can happen multiple times. This works because zone - // registration only keeps a pointer to the struct, it does not copy the data. - for (unsigned int i = 0; i < zone_count; i++) { - malloc_zone_t* zone = reinterpret_cast<malloc_zone_t*>(zones[i]); - if (zone->zone_name && - strcmp(zone->zone_name, kPartitionAllocZoneName) == 0) { - zone->zone_name = "RenamedPartitionAlloc"; - break; - } - } -} - -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -} // namespace partition_alloc
diff --git a/base/allocator/early_zone_registration_mac.h b/base/allocator/early_zone_registration_mac.h deleted file mode 100644 index c8eeb86..0000000 --- a/base/allocator/early_zone_registration_mac.h +++ /dev/null
@@ -1,45 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_EARLY_ZONE_REGISTRATION_MAC_H_ -#define BASE_ALLOCATOR_EARLY_ZONE_REGISTRATION_MAC_H_ - -// This is an Apple-only file, used to register PartitionAlloc's zone *before* -// the process becomes multi-threaded. - -namespace partition_alloc { - -static constexpr char kDelegatingZoneName[] = - "DelegatingDefaultZoneForPartitionAlloc"; -static constexpr char kPartitionAllocZoneName[] = "PartitionAlloc"; - -// Zone version. Determines which callbacks are set in the various malloc_zone_t -// structs. -#if (__MAC_OS_X_VERSION_MAX_ALLOWED >= 130000) || \ - (__IPHONE_OS_VERSION_MAX_ALLOWED >= 160100) -#define PA_TRY_FREE_DEFAULT_IS_AVAILABLE 1 -#endif -#if PA_TRY_FREE_DEFAULT_IS_AVAILABLE -constexpr int kZoneVersion = 13; -#else -constexpr int kZoneVersion = 9; -#endif - -// Must be called *once*, *before* the process becomes multi-threaded. -void EarlyMallocZoneRegistration(); - -// Tricks the registration code to believe that PartitionAlloc was not already -// registered. This allows a future library load to register PartitionAlloc's -// zone as well, rather than bailing out. -// -// This is mutually exclusive with EarlyMallocZoneRegistation(), and should -// ideally be removed. Indeed, by allowing two zones to be registered, we still -// end up with a split heap, and more memory usage. -// -// This is a hack for crbug.com/1274236. -void AllowDoublePartitionAllocZoneRegistration(); - -} // namespace partition_alloc - -#endif // BASE_ALLOCATOR_EARLY_ZONE_REGISTRATION_H_
diff --git a/base/allocator/partition_alloc_features.cc b/base/allocator/partition_alloc_features.cc deleted file mode 100644 index 425f428..0000000 --- a/base/allocator/partition_alloc_features.cc +++ /dev/null
@@ -1,238 +0,0 @@ -// 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. - -#include "base/allocator/partition_alloc_features.h" - -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/base_export.h" -#include "base/feature_list.h" -#include "build/build_config.h" - -namespace base { -namespace features { - -BASE_FEATURE(kPartitionAllocUnretainedDanglingPtr, - "PartitionAllocUnretainedDanglingPtr", - FEATURE_DISABLED_BY_DEFAULT); - -constexpr FeatureParam<UnretainedDanglingPtrMode>::Option - kUnretainedDanglingPtrModeOption[] = { - {UnretainedDanglingPtrMode::kCrash, "crash"}, - {UnretainedDanglingPtrMode::kDumpWithoutCrashing, - "dump_without_crashing"}, -}; -const base::FeatureParam<UnretainedDanglingPtrMode> - kUnretainedDanglingPtrModeParam = { - &kPartitionAllocUnretainedDanglingPtr, - "mode", - UnretainedDanglingPtrMode::kDumpWithoutCrashing, - &kUnretainedDanglingPtrModeOption, -}; - -BASE_FEATURE(kPartitionAllocDanglingPtr, - "PartitionAllocDanglingPtr", -#if BUILDFLAG(ENABLE_DANGLING_RAW_PTR_FEATURE_FLAG) - FEATURE_ENABLED_BY_DEFAULT -#else - FEATURE_DISABLED_BY_DEFAULT -#endif -); - -constexpr FeatureParam<DanglingPtrMode>::Option kDanglingPtrModeOption[] = { - {DanglingPtrMode::kCrash, "crash"}, - {DanglingPtrMode::kLogOnly, "log_only"}, -}; -const base::FeatureParam<DanglingPtrMode> kDanglingPtrModeParam{ - &kPartitionAllocDanglingPtr, - "mode", - DanglingPtrMode::kCrash, - &kDanglingPtrModeOption, -}; -constexpr FeatureParam<DanglingPtrType>::Option kDanglingPtrTypeOption[] = { - {DanglingPtrType::kAll, "all"}, - {DanglingPtrType::kCrossTask, "cross_task"}, -}; -const base::FeatureParam<DanglingPtrType> kDanglingPtrTypeParam{ - &kPartitionAllocDanglingPtr, - "type", - DanglingPtrType::kAll, - &kDanglingPtrTypeOption, -}; - -#if BUILDFLAG(USE_STARSCAN) -// If enabled, PCScan is turned on by default for all partitions that don't -// disable it explicitly. -BASE_FEATURE(kPartitionAllocPCScan, - "PartitionAllocPCScan", - FEATURE_DISABLED_BY_DEFAULT); -#endif // BUILDFLAG(USE_STARSCAN) - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -// If enabled, PCScan is turned on only for the browser's malloc partition. -BASE_FEATURE(kPartitionAllocPCScanBrowserOnly, - "PartitionAllocPCScanBrowserOnly", - FEATURE_DISABLED_BY_DEFAULT); - -// If enabled, PCScan is turned on only for the renderer's malloc partition. -BASE_FEATURE(kPartitionAllocPCScanRendererOnly, - "PartitionAllocPCScanRendererOnly", - FEATURE_DISABLED_BY_DEFAULT); - -// If enabled, this instance belongs to the Control group of the BackupRefPtr -// binary experiment. -BASE_FEATURE(kPartitionAllocBackupRefPtrControl, - "PartitionAllocBackupRefPtrControl", - FEATURE_DISABLED_BY_DEFAULT); - -// Use a larger maximum thread cache cacheable bucket size. -BASE_FEATURE(kPartitionAllocLargeThreadCacheSize, - "PartitionAllocLargeThreadCacheSize", -#if BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_32_BITS) - // Not unconditionally enabled on 32 bit Android, since it is a - // more memory-constrained platform. - FEATURE_DISABLED_BY_DEFAULT -#else - FEATURE_ENABLED_BY_DEFAULT -#endif -); - -BASE_FEATURE(kPartitionAllocLargeEmptySlotSpanRing, - "PartitionAllocLargeEmptySlotSpanRing", - FEATURE_DISABLED_BY_DEFAULT); -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - -BASE_FEATURE(kPartitionAllocBackupRefPtr, - "PartitionAllocBackupRefPtr", -#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN) || \ - BUILDFLAG(ENABLE_BACKUP_REF_PTR_FEATURE_FLAG) || \ - (BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) && BUILDFLAG(IS_LINUX)) - FEATURE_ENABLED_BY_DEFAULT -#else - FEATURE_DISABLED_BY_DEFAULT -#endif -); - -constexpr FeatureParam<BackupRefPtrEnabledProcesses>::Option - kBackupRefPtrEnabledProcessesOptions[] = { - {BackupRefPtrEnabledProcesses::kBrowserOnly, "browser-only"}, - {BackupRefPtrEnabledProcesses::kBrowserAndRenderer, - "browser-and-renderer"}, - {BackupRefPtrEnabledProcesses::kNonRenderer, "non-renderer"}, - {BackupRefPtrEnabledProcesses::kAllProcesses, "all-processes"}}; - -const base::FeatureParam<BackupRefPtrEnabledProcesses> - kBackupRefPtrEnabledProcessesParam { - &kPartitionAllocBackupRefPtr, "enabled-processes", -#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN) || \ - BUILDFLAG(ENABLE_BACKUP_REF_PTR_FEATURE_FLAG) || \ - (BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) && BUILDFLAG(IS_LINUX)) - BackupRefPtrEnabledProcesses::kNonRenderer, -#else - BackupRefPtrEnabledProcesses::kBrowserOnly, -#endif - &kBackupRefPtrEnabledProcessesOptions -}; - -constexpr FeatureParam<BackupRefPtrMode>::Option kBackupRefPtrModeOptions[] = { - {BackupRefPtrMode::kDisabled, "disabled"}, - {BackupRefPtrMode::kEnabled, "enabled"}, - {BackupRefPtrMode::kEnabledWithoutZapping, "enabled-without-zapping"}, - {BackupRefPtrMode::kEnabledWithMemoryReclaimer, - "enabled-with-memory-reclaimer"}, - {BackupRefPtrMode::kDisabledButSplitPartitions2Way, - "disabled-but-2-way-split"}, - {BackupRefPtrMode::kDisabledButSplitPartitions2WayWithMemoryReclaimer, - "disabled-but-2-way-split-with-memory-reclaimer"}, - {BackupRefPtrMode::kDisabledButSplitPartitions3Way, - "disabled-but-3-way-split"}, - {BackupRefPtrMode::kDisabledButAddDummyRefCount, - "disabled-but-add-dummy-ref-count"}, -}; - -const base::FeatureParam<BackupRefPtrMode> kBackupRefPtrModeParam{ - &kPartitionAllocBackupRefPtr, "brp-mode", BackupRefPtrMode::kEnabled, - &kBackupRefPtrModeOptions}; - -const base::FeatureParam<bool> kBackupRefPtrAsanEnableDereferenceCheckParam{ - &kPartitionAllocBackupRefPtr, "asan-enable-dereference-check", true}; -const base::FeatureParam<bool> kBackupRefPtrAsanEnableExtractionCheckParam{ - &kPartitionAllocBackupRefPtr, "asan-enable-extraction-check", - false}; // Not much noise at the moment to enable by default. -const base::FeatureParam<bool> kBackupRefPtrAsanEnableInstantiationCheckParam{ - &kPartitionAllocBackupRefPtr, "asan-enable-instantiation-check", true}; - -// If enabled, switches the bucket distribution to an alternate one. -// -// We enable this by default everywhere except for 32-bit Android, since we saw -// regressions there. -BASE_FEATURE(kPartitionAllocUseAlternateDistribution, - "PartitionAllocUseAlternateDistribution", -#if BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_32_BITS) - FEATURE_DISABLED_BY_DEFAULT -#else - FEATURE_ENABLED_BY_DEFAULT -#endif // BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_32_BITS) -); -const base::FeatureParam<AlternateBucketDistributionMode>::Option - kPartitionAllocAlternateDistributionOption[] = { - {AlternateBucketDistributionMode::kDefault, "default"}, - {AlternateBucketDistributionMode::kDenser, "denser"}, -}; -const base::FeatureParam<AlternateBucketDistributionMode> - kPartitionAllocAlternateBucketDistributionParam{ - &kPartitionAllocUseAlternateDistribution, "mode", - AlternateBucketDistributionMode::kDefault, - &kPartitionAllocAlternateDistributionOption}; - -// Configures whether we set a lower limit for renderers that do not have a main -// frame, similar to the limit that is already done for backgrounded renderers. -BASE_FEATURE(kLowerPAMemoryLimitForNonMainRenderers, - "LowerPAMemoryLimitForNonMainRenderers", - FEATURE_DISABLED_BY_DEFAULT); - -// If enabled, switches PCScan scheduling to a mutator-aware scheduler. Does not -// affect whether PCScan is enabled itself. -BASE_FEATURE(kPartitionAllocPCScanMUAwareScheduler, - "PartitionAllocPCScanMUAwareScheduler", - FEATURE_ENABLED_BY_DEFAULT); - -// If enabled, PCScan frees unconditionally all quarantined objects. -// This is a performance testing feature. -BASE_FEATURE(kPartitionAllocPCScanImmediateFreeing, - "PartitionAllocPCScanImmediateFreeing", - FEATURE_DISABLED_BY_DEFAULT); - -// If enabled, PCScan clears eagerly (synchronously) on free(). -BASE_FEATURE(kPartitionAllocPCScanEagerClearing, - "PartitionAllocPCScanEagerClearing", - FEATURE_DISABLED_BY_DEFAULT); - -// In addition to heap, scan also the stack of the current mutator. -BASE_FEATURE(kPartitionAllocPCScanStackScanning, - "PartitionAllocPCScanStackScanning", -#if BUILDFLAG(PCSCAN_STACK_SUPPORTED) - FEATURE_ENABLED_BY_DEFAULT -#else - FEATURE_DISABLED_BY_DEFAULT -#endif // BUILDFLAG(PCSCAN_STACK_SUPPORTED) -); - -BASE_FEATURE(kPartitionAllocDCScan, - "PartitionAllocDCScan", - FEATURE_DISABLED_BY_DEFAULT); - -// Whether to sort the active slot spans in PurgeMemory(). -BASE_FEATURE(kPartitionAllocSortActiveSlotSpans, - "PartitionAllocSortActiveSlotSpans", - FEATURE_DISABLED_BY_DEFAULT); - -#if BUILDFLAG(IS_WIN) -// Whether to retry allocations when commit fails. -BASE_FEATURE(kPageAllocatorRetryOnCommitFailure, - "PageAllocatorRetryOnCommitFailure", - FEATURE_DISABLED_BY_DEFAULT); -#endif - -} // namespace features -} // namespace base
diff --git a/base/allocator/partition_alloc_features.h b/base/allocator/partition_alloc_features.h deleted file mode 100644 index 7398916..0000000 --- a/base/allocator/partition_alloc_features.h +++ /dev/null
@@ -1,148 +0,0 @@ -// 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 BASE_ALLOCATOR_PARTITION_ALLOC_FEATURES_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOC_FEATURES_H_ - -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/base_export.h" -#include "base/compiler_specific.h" -#include "base/feature_list.h" -#include "base/metrics/field_trial_params.h" -#include "build/build_config.h" - -namespace base { -namespace features { - -extern const BASE_EXPORT Feature kPartitionAllocUnretainedDanglingPtr; -enum class UnretainedDanglingPtrMode { - kCrash, - kDumpWithoutCrashing, -}; -extern const BASE_EXPORT base::FeatureParam<UnretainedDanglingPtrMode> - kUnretainedDanglingPtrModeParam; - -// See /docs/dangling_ptr.md -BASE_EXPORT BASE_DECLARE_FEATURE(kPartitionAllocDanglingPtr); -enum class DanglingPtrMode { - // Crash immediately after detecting a dangling raw_ptr. - kCrash, // (default) - - // Log the signature of every occurrences without crashing. It is used by - // bots. - // Format "[DanglingSignature]\t<1>\t<2>\t<3>\t<4>" - // 1. The function which freed the memory while it was still referenced. - // 2. The task in which the memory was freed. - // 3. The function which released the raw_ptr reference. - // 4. The task in which the raw_ptr was released. - kLogOnly, - - // Note: This will be extended with a single shot DumpWithoutCrashing. -}; -extern const BASE_EXPORT base::FeatureParam<DanglingPtrMode> - kDanglingPtrModeParam; -enum class DanglingPtrType { - // Act on any dangling raw_ptr released after being freed. - kAll, // (default) - - // Detect when freeing memory and releasing the dangling raw_ptr happens in - // a different task. Those are more likely to cause use after free. - kCrossTask, - - // Note: This will be extended with LongLived -}; -extern const BASE_EXPORT base::FeatureParam<DanglingPtrType> - kDanglingPtrTypeParam; - -#if BUILDFLAG(USE_STARSCAN) -BASE_EXPORT BASE_DECLARE_FEATURE(kPartitionAllocPCScan); -#endif -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -BASE_EXPORT BASE_DECLARE_FEATURE(kPartitionAllocPCScanBrowserOnly); -BASE_EXPORT BASE_DECLARE_FEATURE(kPartitionAllocPCScanRendererOnly); -BASE_EXPORT BASE_DECLARE_FEATURE(kPartitionAllocBackupRefPtrControl); -BASE_EXPORT BASE_DECLARE_FEATURE(kPartitionAllocLargeThreadCacheSize); -BASE_EXPORT BASE_DECLARE_FEATURE(kPartitionAllocLargeEmptySlotSpanRing); -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - -enum class BackupRefPtrEnabledProcesses { - // BRP enabled only in the browser process. - kBrowserOnly, - // BRP enabled only in the browser and renderer processes. - kBrowserAndRenderer, - // BRP enabled in all processes, except renderer. - kNonRenderer, - // BRP enabled in all processes. - kAllProcesses, -}; - -enum class BackupRefPtrMode { - // BRP is disabled across all partitions. Equivalent to the Finch flag being - // disabled. - kDisabled, - - // BRP is enabled in the main partition, as well as certain Renderer-only - // partitions (if enabled in Renderer at all). - // This entails splitting the main partition. - kEnabled, - - // Same as kEnabled but without zapping quarantined objects. - kEnabledWithoutZapping, - - // Same as kEnabled but registers the main partition to memory reclaimer. - kEnabledWithMemoryReclaimer, - - // BRP is disabled, but the main partition is split out, as if BRP was enabled - // in the "previous slot" mode. - kDisabledButSplitPartitions2Way, - - // Same as kDisabledButSplitPartitions2Way but registers the main partition to - // memory reclaimer. - kDisabledButSplitPartitions2WayWithMemoryReclaimer, - - // BRP is disabled, but the main partition *and* aligned partition are split - // out, as if BRP was enabled in the "before allocation" mode. - kDisabledButSplitPartitions3Way, - - // BRP is disabled, but add dummy ref count to each allocation. This will - // increase allocation size but not change any of the logic. If an issue - // reproduce in this mode, it means the increase in size is causing it. - kDisabledButAddDummyRefCount, -}; - -enum class AlternateBucketDistributionMode : uint8_t { - kDefault, - kDenser, -}; - -BASE_EXPORT BASE_DECLARE_FEATURE(kPartitionAllocBackupRefPtr); -extern const BASE_EXPORT base::FeatureParam<BackupRefPtrEnabledProcesses> - kBackupRefPtrEnabledProcessesParam; -extern const BASE_EXPORT base::FeatureParam<BackupRefPtrMode> - kBackupRefPtrModeParam; -extern const BASE_EXPORT base::FeatureParam<bool> - kBackupRefPtrAsanEnableDereferenceCheckParam; -extern const BASE_EXPORT base::FeatureParam<bool> - kBackupRefPtrAsanEnableExtractionCheckParam; -extern const BASE_EXPORT base::FeatureParam<bool> - kBackupRefPtrAsanEnableInstantiationCheckParam; -extern const BASE_EXPORT base::FeatureParam<AlternateBucketDistributionMode> - kPartitionAllocAlternateBucketDistributionParam; - -BASE_EXPORT BASE_DECLARE_FEATURE(kLowerPAMemoryLimitForNonMainRenderers); -BASE_EXPORT BASE_DECLARE_FEATURE(kPartitionAllocPCScanMUAwareScheduler); -BASE_EXPORT BASE_DECLARE_FEATURE(kPartitionAllocPCScanStackScanning); -BASE_EXPORT BASE_DECLARE_FEATURE(kPartitionAllocDCScan); -BASE_EXPORT BASE_DECLARE_FEATURE(kPartitionAllocPCScanImmediateFreeing); -BASE_EXPORT BASE_DECLARE_FEATURE(kPartitionAllocPCScanEagerClearing); -BASE_EXPORT BASE_DECLARE_FEATURE(kPartitionAllocSortActiveSlotSpans); -BASE_EXPORT BASE_DECLARE_FEATURE(kPartitionAllocUseAlternateDistribution); -#if BUILDFLAG(IS_WIN) -BASE_EXPORT BASE_DECLARE_FEATURE(kPageAllocatorRetryOnCommitFailure); -#endif - -} // namespace features -} // namespace base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOC_FEATURES_H_
diff --git a/base/allocator/partition_alloc_support.cc b/base/allocator/partition_alloc_support.cc deleted file mode 100644 index d2d56a9..0000000 --- a/base/allocator/partition_alloc_support.cc +++ /dev/null
@@ -1,1316 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_alloc_support.h" - -#include <array> -#include <cinttypes> -#include <cstdint> -#include <map> -#include <string> - -#include "base/allocator/partition_alloc_features.h" -#include "base/allocator/partition_allocator/allocation_guard.h" -#include "base/allocator/partition_allocator/dangling_raw_ptr_checks.h" -#include "base/allocator/partition_allocator/memory_reclaimer.h" -#include "base/allocator/partition_allocator/page_allocator.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/alias.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/partition_alloc_config.h" -#include "base/allocator/partition_allocator/partition_lock.h" -#include "base/allocator/partition_allocator/shim/allocator_shim.h" -#include "base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_partition_alloc.h" -#include "base/allocator/partition_allocator/thread_cache.h" -#include "base/check.h" -#include "base/debug/dump_without_crashing.h" -#include "base/debug/stack_trace.h" -#include "base/debug/task_trace.h" -#include "base/feature_list.h" -#include "base/functional/bind.h" -#include "base/functional/callback.h" -#include "base/immediate_crash.h" -#include "base/location.h" -#include "base/memory/raw_ptr_asan_service.h" -#include "base/metrics/histogram_functions.h" -#include "base/metrics/histogram_macros.h" -#include "base/no_destructor.h" -#include "base/strings/string_piece.h" -#include "base/strings/string_split.h" -#include "base/strings/stringprintf.h" -#include "base/system/sys_info.h" -#include "base/task/single_thread_task_runner.h" -#include "base/thread_annotations.h" -#include "base/threading/platform_thread.h" -#include "base/time/time.h" -#include "base/timer/timer.h" -#include "base/trace_event/base_tracing.h" -#include "build/build_config.h" -#include "third_party/abseil-cpp/absl/types/optional.h" - -#if BUILDFLAG(USE_STARSCAN) -#include "base/allocator/partition_allocator/starscan/pcscan.h" -#include "base/allocator/partition_allocator/starscan/pcscan_scheduling.h" -#include "base/allocator/partition_allocator/starscan/stack/stack.h" -#include "base/allocator/partition_allocator/starscan/stats_collector.h" -#include "base/allocator/partition_allocator/starscan/stats_reporter.h" -#include "base/memory/nonscannable_memory.h" -#endif // BUILDFLAG(USE_STARSCAN) - -#if BUILDFLAG(IS_ANDROID) -#include "base/system/sys_info.h" -#endif - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -#include "base/allocator/partition_allocator/memory_reclaimer.h" -#endif - -namespace base::allocator { - -namespace { - -// This is defined in content/public/common/content_switches.h, which is not -// accessible in ::base. They must be kept in sync. -namespace switches { -[[maybe_unused]] constexpr char kRendererProcess[] = "renderer"; -constexpr char kZygoteProcess[] = "zygote"; -#if BUILDFLAG(USE_STARSCAN) -constexpr char kGpuProcess[] = "gpu-process"; -constexpr char kUtilityProcess[] = "utility"; -#endif -} // namespace switches - -#if BUILDFLAG(USE_STARSCAN) - -#if BUILDFLAG(ENABLE_BASE_TRACING) -constexpr const char* ScannerIdToTracingString( - partition_alloc::internal::StatsCollector::ScannerId id) { - switch (id) { - case partition_alloc::internal::StatsCollector::ScannerId::kClear: - return "PCScan.Scanner.Clear"; - case partition_alloc::internal::StatsCollector::ScannerId::kScan: - return "PCScan.Scanner.Scan"; - case partition_alloc::internal::StatsCollector::ScannerId::kSweep: - return "PCScan.Scanner.Sweep"; - case partition_alloc::internal::StatsCollector::ScannerId::kOverall: - return "PCScan.Scanner"; - case partition_alloc::internal::StatsCollector::ScannerId::kNumIds: - __builtin_unreachable(); - } -} - -constexpr const char* MutatorIdToTracingString( - partition_alloc::internal::StatsCollector::MutatorId id) { - switch (id) { - case partition_alloc::internal::StatsCollector::MutatorId::kClear: - return "PCScan.Mutator.Clear"; - case partition_alloc::internal::StatsCollector::MutatorId::kScanStack: - return "PCScan.Mutator.ScanStack"; - case partition_alloc::internal::StatsCollector::MutatorId::kScan: - return "PCScan.Mutator.Scan"; - case partition_alloc::internal::StatsCollector::MutatorId::kOverall: - return "PCScan.Mutator"; - case partition_alloc::internal::StatsCollector::MutatorId::kNumIds: - __builtin_unreachable(); - } -} -#endif // BUILDFLAG(ENABLE_BASE_TRACING) - -// Inject TRACE_EVENT_BEGIN/END, TRACE_COUNTER1, and UmaHistogramTimes. -class StatsReporterImpl final : public partition_alloc::StatsReporter { - public: - void ReportTraceEvent( - partition_alloc::internal::StatsCollector::ScannerId id, - [[maybe_unused]] partition_alloc::internal::base::PlatformThreadId tid, - int64_t start_time_ticks_internal_value, - int64_t end_time_ticks_internal_value) override { -#if BUILDFLAG(ENABLE_BASE_TRACING) - // TRACE_EVENT_* macros below drop most parameters when tracing is - // disabled at compile time. - const char* tracing_id = ScannerIdToTracingString(id); - const TimeTicks start_time = - TimeTicks::FromInternalValue(start_time_ticks_internal_value); - const TimeTicks end_time = - TimeTicks::FromInternalValue(end_time_ticks_internal_value); - TRACE_EVENT_BEGIN(kTraceCategory, perfetto::StaticString(tracing_id), - perfetto::ThreadTrack::ForThread(tid), start_time); - TRACE_EVENT_END(kTraceCategory, perfetto::ThreadTrack::ForThread(tid), - end_time); -#endif // BUILDFLAG(ENABLE_BASE_TRACING) - } - - void ReportTraceEvent( - partition_alloc::internal::StatsCollector::MutatorId id, - [[maybe_unused]] partition_alloc::internal::base::PlatformThreadId tid, - int64_t start_time_ticks_internal_value, - int64_t end_time_ticks_internal_value) override { -#if BUILDFLAG(ENABLE_BASE_TRACING) - // TRACE_EVENT_* macros below drop most parameters when tracing is - // disabled at compile time. - const char* tracing_id = MutatorIdToTracingString(id); - const TimeTicks start_time = - TimeTicks::FromInternalValue(start_time_ticks_internal_value); - const TimeTicks end_time = - TimeTicks::FromInternalValue(end_time_ticks_internal_value); - TRACE_EVENT_BEGIN(kTraceCategory, perfetto::StaticString(tracing_id), - perfetto::ThreadTrack::ForThread(tid), start_time); - TRACE_EVENT_END(kTraceCategory, perfetto::ThreadTrack::ForThread(tid), - end_time); -#endif // BUILDFLAG(ENABLE_BASE_TRACING) - } - - void ReportSurvivedQuarantineSize(size_t survived_size) override { - TRACE_COUNTER1(kTraceCategory, "PCScan.SurvivedQuarantineSize", - survived_size); - } - - void ReportSurvivedQuarantinePercent(double survived_rate) override { - // Multiply by 1000 since TRACE_COUNTER1 expects integer. In catapult, - // divide back. - // TODO(bikineev): Remove after switching to perfetto. - TRACE_COUNTER1(kTraceCategory, "PCScan.SurvivedQuarantinePercent", - 1000 * survived_rate); - } - - void ReportStats(const char* stats_name, int64_t sample_in_usec) override { - TimeDelta sample = Microseconds(sample_in_usec); - UmaHistogramTimes(stats_name, sample); - } - - private: - static constexpr char kTraceCategory[] = "partition_alloc"; -}; - -#endif // BUILDFLAG(USE_STARSCAN) - -} // namespace - -#if BUILDFLAG(USE_STARSCAN) -void RegisterPCScanStatsReporter() { - static StatsReporterImpl s_reporter; - static bool registered = false; - - DCHECK(!registered); - - partition_alloc::internal::PCScan::RegisterStatsReporter(&s_reporter); - registered = true; -} -#endif // BUILDFLAG(USE_STARSCAN) - -namespace { - -void RunThreadCachePeriodicPurge() { - // Micros, since periodic purge should typically take at most a few ms. - SCOPED_UMA_HISTOGRAM_TIMER_MICROS("Memory.PartitionAlloc.PeriodicPurge"); - TRACE_EVENT0("memory", "PeriodicPurge"); - auto& instance = ::partition_alloc::ThreadCacheRegistry::Instance(); - instance.RunPeriodicPurge(); - TimeDelta delay = - Microseconds(instance.GetPeriodicPurgeNextIntervalInMicroseconds()); - SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask( - FROM_HERE, BindOnce(RunThreadCachePeriodicPurge), delay); -} - -void RunMemoryReclaimer(scoped_refptr<SequencedTaskRunner> task_runner) { - TRACE_EVENT0("base", "partition_alloc::MemoryReclaimer::Reclaim()"); - auto* instance = ::partition_alloc::MemoryReclaimer::Instance(); - - { - // Micros, since memory reclaiming should typically take at most a few ms. - SCOPED_UMA_HISTOGRAM_TIMER_MICROS("Memory.PartitionAlloc.MemoryReclaim"); - instance->ReclaimNormal(); - } - - TimeDelta delay = - Microseconds(instance->GetRecommendedReclaimIntervalInMicroseconds()); - task_runner->PostDelayedTask( - FROM_HERE, BindOnce(RunMemoryReclaimer, task_runner), delay); -} - -} // namespace - -void StartThreadCachePeriodicPurge() { - auto& instance = ::partition_alloc::ThreadCacheRegistry::Instance(); - TimeDelta delay = - Microseconds(instance.GetPeriodicPurgeNextIntervalInMicroseconds()); - SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask( - FROM_HERE, BindOnce(RunThreadCachePeriodicPurge), delay); -} - -void StartMemoryReclaimer(scoped_refptr<SequencedTaskRunner> task_runner) { - // Can be called several times. - static bool is_memory_reclaimer_running = false; - if (is_memory_reclaimer_running) { - return; - } - is_memory_reclaimer_running = true; - - // The caller of the API fully controls where running the reclaim. - // However there are a few reasons to recommend that the caller runs - // it on the main thread: - // - Most of PartitionAlloc's usage is on the main thread, hence PA's metadata - // is more likely in cache when executing on the main thread. - // - Memory reclaim takes the partition lock for each partition. As a - // consequence, while reclaim is running, the main thread is unlikely to be - // able to make progress, as it would be waiting on the lock. - // - Finally, this runs in idle time only, so there should be no visible - // impact. - // - // From local testing, time to reclaim is 100us-1ms, and reclaiming every few - // seconds is useful. Since this is meant to run during idle time only, it is - // a reasonable starting point balancing effectivenes vs cost. See - // crbug.com/942512 for details and experimental results. - auto* instance = ::partition_alloc::MemoryReclaimer::Instance(); - TimeDelta delay = - Microseconds(instance->GetRecommendedReclaimIntervalInMicroseconds()); - task_runner->PostDelayedTask( - FROM_HERE, BindOnce(RunMemoryReclaimer, task_runner), delay); -} - -std::map<std::string, std::string> ProposeSyntheticFinchTrials() { - std::map<std::string, std::string> trials; - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - // BackupRefPtr_Effective and PCScan_Effective record whether or not - // BackupRefPtr and/or PCScan are enabled. The experiments aren't independent, - // so having a synthetic Finch will help look only at cases where one isn't - // affected by the other. - - // Whether PartitionAllocBackupRefPtr is enabled (as determined by - // FeatureList::IsEnabled). - [[maybe_unused]] bool brp_finch_enabled = false; - // Whether PartitionAllocBackupRefPtr is set up for the default behavior. The - // default behavior is when either the Finch flag is disabled, or is enabled - // in brp-mode=disabled (these two options are equivalent). - [[maybe_unused]] bool brp_nondefault_behavior = false; - // Whether PartitionAllocBackupRefPtr is set up to enable BRP protection. It - // requires the Finch flag to be enabled and brp-mode!=disabled*. Some modes, - // e.g. disabled-but-3-way-split, do something (hence can't be considered the - // default behavior), but don't enable BRP protection. - [[maybe_unused]] bool brp_truly_enabled = false; -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - if (FeatureList::IsEnabled(features::kPartitionAllocBackupRefPtr)) { - brp_finch_enabled = true; - } - if (brp_finch_enabled && features::kBackupRefPtrModeParam.Get() != - features::BackupRefPtrMode::kDisabled) { - brp_nondefault_behavior = true; - } - if (brp_finch_enabled && features::kBackupRefPtrModeParam.Get() == - features::BackupRefPtrMode::kEnabled) { - brp_truly_enabled = true; - } -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - [[maybe_unused]] bool pcscan_enabled = -#if BUILDFLAG(USE_STARSCAN) - FeatureList::IsEnabled(features::kPartitionAllocPCScanBrowserOnly); -#else - false; -#endif - - std::string brp_group_name = "Unavailable"; -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - if (pcscan_enabled) { - // If PCScan is enabled, just ignore the population. - brp_group_name = "Ignore_PCScanIsOn"; - } else if (!brp_finch_enabled) { - // The control group is actually disguised as "enabled", but in fact it's - // disabled using a param. This is to differentiate the population that - // participates in the control group, from the population that isn't in any - // group. - brp_group_name = "Ignore_NoGroup"; - } else { - switch (features::kBackupRefPtrModeParam.Get()) { - case features::BackupRefPtrMode::kDisabled: - brp_group_name = "Disabled"; - break; - case features::BackupRefPtrMode::kEnabled: -#if BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) - brp_group_name = "EnabledPrevSlot"; -#else - brp_group_name = "EnabledBeforeAlloc"; -#endif - break; - case features::BackupRefPtrMode::kEnabledWithoutZapping: -#if BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) - brp_group_name = "EnabledPrevSlotWithoutZapping"; -#else - brp_group_name = "EnabledBeforeAllocWithoutZapping"; -#endif - break; - case features::BackupRefPtrMode::kEnabledWithMemoryReclaimer: -#if BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) - brp_group_name = "EnabledPrevSlotWithMemoryReclaimer"; -#else - brp_group_name = "EnabledBeforeAllocWithMemoryReclaimer"; -#endif - break; - case features::BackupRefPtrMode::kDisabledButSplitPartitions2Way: - brp_group_name = "DisabledBut2WaySplit"; - break; - case features::BackupRefPtrMode:: - kDisabledButSplitPartitions2WayWithMemoryReclaimer: - brp_group_name = "DisabledBut2WaySplitWithMemoryReclaimer"; - break; - case features::BackupRefPtrMode::kDisabledButSplitPartitions3Way: - brp_group_name = "DisabledBut3WaySplit"; - break; - case features::BackupRefPtrMode::kDisabledButAddDummyRefCount: - brp_group_name = "DisabledButAddDummyRefCount"; - break; - } - - if (features::kBackupRefPtrModeParam.Get() != - features::BackupRefPtrMode::kDisabled) { - std::string process_selector; - switch (features::kBackupRefPtrEnabledProcessesParam.Get()) { - case features::BackupRefPtrEnabledProcesses::kBrowserOnly: - process_selector = "BrowserOnly"; - break; - case features::BackupRefPtrEnabledProcesses::kBrowserAndRenderer: - process_selector = "BrowserAndRenderer"; - break; - case features::BackupRefPtrEnabledProcesses::kNonRenderer: - process_selector = "NonRenderer"; - break; - case features::BackupRefPtrEnabledProcesses::kAllProcesses: - process_selector = "AllProcesses"; - break; - } - - brp_group_name += ("_" + process_selector); - } - } -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - trials.emplace("BackupRefPtr_Effective", brp_group_name); - - // On 32-bit architectures, PCScan is not supported and permanently disabled. - // Don't lump it into "Disabled", so that belonging to "Enabled"/"Disabled" is - // fully controlled by Finch and thus have identical population sizes. - std::string pcscan_group_name = "Unavailable"; - std::string pcscan_group_name_fallback = "Unavailable"; -#if BUILDFLAG(USE_STARSCAN) - if (brp_truly_enabled) { - // If BRP protection is enabled, just ignore the population. Check - // brp_truly_enabled, not brp_finch_enabled, because there are certain modes - // where BRP protection is actually disabled. - pcscan_group_name = "Ignore_BRPIsOn"; - } else { - pcscan_group_name = (pcscan_enabled ? "Enabled" : "Disabled"); - } - // In case we are incorrect that PCScan is independent of partition-split - // modes, create a fallback trial that only takes into account the BRP Finch - // settings that preserve the default behavior. - if (brp_nondefault_behavior) { - pcscan_group_name_fallback = "Ignore_BRPIsOn"; - } else { - pcscan_group_name_fallback = (pcscan_enabled ? "Enabled" : "Disabled"); - } -#endif // BUILDFLAG(USE_STARSCAN) - trials.emplace("PCScan_Effective", pcscan_group_name); - trials.emplace("PCScan_Effective_Fallback", pcscan_group_name_fallback); -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - -#if BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) - trials.emplace("DanglingPointerDetector", "Enabled"); -#else - trials.emplace("DanglingPointerDetector", "Disabled"); -#endif - - return trials; -} - -#if BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) - -namespace { - -internal::PartitionLock g_stack_trace_buffer_lock; - -struct DanglingPointerFreeInfo { - debug::StackTrace stack_trace; - debug::TaskTrace task_trace; - uintptr_t id = 0; -}; -using DanglingRawPtrBuffer = - std::array<absl::optional<DanglingPointerFreeInfo>, 32>; -DanglingRawPtrBuffer g_stack_trace_buffer GUARDED_BY(g_stack_trace_buffer_lock); - -void DanglingRawPtrDetected(uintptr_t id) { - // This is called from inside the allocator. No allocation is allowed. - - internal::PartitionAutoLock guard(g_stack_trace_buffer_lock); - -#if DCHECK_IS_ON() - for (absl::optional<DanglingPointerFreeInfo>& entry : g_stack_trace_buffer) { - PA_DCHECK(!entry || entry->id != id); - } -#endif // DCHECK_IS_ON() - - for (absl::optional<DanglingPointerFreeInfo>& entry : g_stack_trace_buffer) { - if (!entry) { - entry = {debug::StackTrace(), debug::TaskTrace(), id}; - return; - } - } - - // The StackTrace hasn't been recorded, because the buffer isn't large - // enough. -} - -// From the traces recorded in |DanglingRawPtrDetected|, extract the one -// whose id match |id|. Return nullopt if not found. -absl::optional<DanglingPointerFreeInfo> TakeDanglingPointerFreeInfo( - uintptr_t id) { - internal::PartitionAutoLock guard(g_stack_trace_buffer_lock); - for (absl::optional<DanglingPointerFreeInfo>& entry : g_stack_trace_buffer) { - if (entry && entry->id == id) { - absl::optional<DanglingPointerFreeInfo> result(entry); - entry = absl::nullopt; - return result; - } - } - return absl::nullopt; -} - -// Extract from the StackTrace output, the signature of the pertinent caller. -// This function is meant to be used only by Chromium developers, to list what -// are all the dangling raw_ptr occurrences in a table. -std::string ExtractDanglingPtrSignature(std::string stacktrace) { - std::vector<StringPiece> lines = SplitStringPiece( - stacktrace, "\r\n", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY); - - // We are looking for the callers of the function releasing the raw_ptr and - // freeing memory: - const StringPiece callees[] = { - // Common signatures - "internal::PartitionFree", - "base::(anonymous namespace)::FreeFn", - - // Linux signatures - "internal::RawPtrBackupRefImpl<>::ReleaseInternal()", - "base::RefCountedThreadSafe<>::Release()", - - // Windows signatures - "internal::RawPtrBackupRefImpl<0>::ReleaseInternal", - "_free_base", - // Windows stack traces are prefixed with "Backtrace:" - "Backtrace:", - - // Mac signatures - "internal::RawPtrBackupRefImpl<false>::ReleaseInternal", - - // Task traces are prefixed with "Task trace:" in - // |TaskTrace::OutputToStream| - "Task trace:", - }; - size_t caller_index = 0; - for (size_t i = 0; i < lines.size(); ++i) { - for (const auto& callee : callees) { - if (lines[i].find(callee) != StringPiece::npos) { - caller_index = i + 1; - } - } - } - if (caller_index >= lines.size()) { - return "no_callee_match"; - } - StringPiece caller = lines[caller_index]; - - if (caller.empty()) { - return "invalid_format"; - } - - // On Posix platforms |callers| follows the following format: - // - // #<index> <address> <symbol> - // - // See https://crsrc.org/c/base/debug/stack_trace_posix.cc - if (caller[0] == '#') { - const size_t address_start = caller.find(' '); - const size_t function_start = caller.find(' ', address_start + 1); - - if (address_start == caller.npos || function_start == caller.npos) { - return "invalid_format"; - } - - return std::string(caller.substr(function_start + 1)); - } - - // On Windows platforms |callers| follows the following format: - // - // \t<symbol> [0x<address>]+<displacement>(<filename>:<line>) - // - // See https://crsrc.org/c/base/debug/stack_trace_win.cc - if (caller[0] == '\t') { - const size_t symbol_start = 1; - const size_t symbol_end = caller.find(' '); - if (symbol_end == caller.npos) { - return "invalid_format"; - } - return std::string(caller.substr(symbol_start, symbol_end - symbol_start)); - } - - // On Mac platforms |callers| follows the following format: - // - // <index> <library> 0x<address> <symbol> + <line> - // - // See https://crsrc.org/c/base/debug/stack_trace_posix.cc - if (caller[0] >= '0' && caller[0] <= '9') { - const size_t address_start = caller.find("0x"); - const size_t symbol_start = caller.find(' ', address_start + 1) + 1; - const size_t symbol_end = caller.find(' ', symbol_start); - if (symbol_start == caller.npos || symbol_end == caller.npos) { - return "invalid_format"; - } - return std::string(caller.substr(symbol_start, symbol_end - symbol_start)); - } - - return "invalid_format"; -} - -std::string ExtractDanglingPtrSignature(debug::TaskTrace task_trace) { - if (task_trace.empty()) { - return "No active task"; - } - return ExtractDanglingPtrSignature(task_trace.ToString()); -} - -std::string ExtractDanglingPtrSignature( - absl::optional<DanglingPointerFreeInfo> free_info, - debug::StackTrace release_stack_trace, - debug::TaskTrace release_task_trace) { - if (free_info) { - return StringPrintf( - "[DanglingSignature]\t%s\t%s\t%s\t%s", - ExtractDanglingPtrSignature(free_info->stack_trace.ToString()).c_str(), - ExtractDanglingPtrSignature(free_info->task_trace).c_str(), - ExtractDanglingPtrSignature(release_stack_trace.ToString()).c_str(), - ExtractDanglingPtrSignature(release_task_trace).c_str()); - } - return StringPrintf( - "[DanglingSignature]\t%s\t%s\t%s\t%s", "missing", "missing", - ExtractDanglingPtrSignature(release_stack_trace.ToString()).c_str(), - ExtractDanglingPtrSignature(release_task_trace).c_str()); -} - -template <features::DanglingPtrMode dangling_pointer_mode, - features::DanglingPtrType dangling_pointer_type> -void DanglingRawPtrReleased(uintptr_t id) { - // This is called from raw_ptr<>'s release operation. Making allocations is - // allowed. In particular, symbolizing and printing the StackTraces may - // allocate memory. - debug::StackTrace stack_trace_release; - debug::TaskTrace task_trace_release; - absl::optional<DanglingPointerFreeInfo> free_info = - TakeDanglingPointerFreeInfo(id); - - if constexpr (dangling_pointer_type == - features::DanglingPtrType::kCrossTask) { - if (!free_info) { - return; - } - if (task_trace_release.ToString() == free_info->task_trace.ToString()) { - return; - } - } - - std::string dangling_signature = ExtractDanglingPtrSignature( - free_info, stack_trace_release, task_trace_release); - static const char dangling_ptr_footer[] = - "\n" - "\n" - "Please check for more information on:\n" - "https://chromium.googlesource.com/chromium/src/+/main/docs/" - "dangling_ptr_guide.md\n" - "\n" - "Googlers: Please give us your feedback about the dangling pointer\n" - " detector at:\n" - " http://go/dangling-ptr-cq-survey\n"; - if (free_info) { - LOG(ERROR) << "Detected dangling raw_ptr with id=" - << StringPrintf("0x%016" PRIxPTR, id) << ":\n" - << dangling_signature << "\n\n" - << "The memory was freed at:\n" - << free_info->stack_trace << "\n" - << free_info->task_trace << "\n" - << "The dangling raw_ptr was released at:\n" - << stack_trace_release << "\n" - << task_trace_release << dangling_ptr_footer; - } else { - LOG(ERROR) << "Detected dangling raw_ptr with id=" - << StringPrintf("0x%016" PRIxPTR, id) << ":\n\n" - << dangling_signature << "\n\n" - << "It was not recorded where the memory was freed.\n\n" - << "The dangling raw_ptr was released at:\n" - << stack_trace_release << "\n" - << task_trace_release << dangling_ptr_footer; - } - - if constexpr (dangling_pointer_mode == features::DanglingPtrMode::kCrash) { - ImmediateCrash(); - } -} - -void ClearDanglingRawPtrBuffer() { - internal::PartitionAutoLock guard(g_stack_trace_buffer_lock); - g_stack_trace_buffer = DanglingRawPtrBuffer(); -} - -} // namespace - -void InstallDanglingRawPtrChecks() { - // Clearing storage is useful for running multiple unit tests without - // restarting the test executable. - ClearDanglingRawPtrBuffer(); - - if (!FeatureList::IsEnabled(features::kPartitionAllocDanglingPtr)) { - partition_alloc::SetDanglingRawPtrDetectedFn([](uintptr_t) {}); - partition_alloc::SetDanglingRawPtrReleasedFn([](uintptr_t) {}); - return; - } - - partition_alloc::SetDanglingRawPtrDetectedFn(&DanglingRawPtrDetected); - switch (features::kDanglingPtrModeParam.Get()) { - case features::DanglingPtrMode::kCrash: - switch (features::kDanglingPtrTypeParam.Get()) { - case features::DanglingPtrType::kAll: - partition_alloc::SetDanglingRawPtrReleasedFn( - &DanglingRawPtrReleased<features::DanglingPtrMode::kCrash, - features::DanglingPtrType::kAll>); - break; - case features::DanglingPtrType::kCrossTask: - partition_alloc::SetDanglingRawPtrReleasedFn( - &DanglingRawPtrReleased<features::DanglingPtrMode::kCrash, - features::DanglingPtrType::kCrossTask>); - break; - } - break; - case features::DanglingPtrMode::kLogOnly: - switch (features::kDanglingPtrTypeParam.Get()) { - case features::DanglingPtrType::kAll: - partition_alloc::SetDanglingRawPtrReleasedFn( - &DanglingRawPtrReleased<features::DanglingPtrMode::kLogOnly, - features::DanglingPtrType::kAll>); - break; - case features::DanglingPtrType::kCrossTask: - partition_alloc::SetDanglingRawPtrReleasedFn( - &DanglingRawPtrReleased<features::DanglingPtrMode::kLogOnly, - features::DanglingPtrType::kCrossTask>); - break; - } - break; - } -} - -// TODO(arthursonzogni): There might exist long lived dangling raw_ptr. If there -// is a dangling pointer, we should crash at some point. Consider providing an -// API to periodically check the buffer. - -#else // BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) -void InstallDanglingRawPtrChecks() {} -#endif // BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) - -void UnretainedDanglingRawPtrDetectedDumpWithoutCrashing(uintptr_t id) { - PA_NO_CODE_FOLDING(); - debug::DumpWithoutCrashing(); -} - -void UnretainedDanglingRawPtrDetectedCrash(uintptr_t id) { - debug::TaskTrace task_trace; - debug::StackTrace stack_trace; - LOG(ERROR) << "Detected dangling raw_ptr in unretained with id=" - << StringPrintf("0x%016" PRIxPTR, id) << ":\n\n" - << task_trace << stack_trace; - ImmediateCrash(); -} - -void InstallUnretainedDanglingRawPtrChecks() { - if (!FeatureList::IsEnabled(features::kPartitionAllocUnretainedDanglingPtr)) { - partition_alloc::SetUnretainedDanglingRawPtrDetectedFn([](uintptr_t) {}); - partition_alloc::SetUnretainedDanglingRawPtrCheckEnabled(/*enabled=*/false); - return; - } - - partition_alloc::SetUnretainedDanglingRawPtrCheckEnabled(/*enabled=*/true); - switch (features::kUnretainedDanglingPtrModeParam.Get()) { - case features::UnretainedDanglingPtrMode::kCrash: - partition_alloc::SetUnretainedDanglingRawPtrDetectedFn( - &UnretainedDanglingRawPtrDetectedCrash); - break; - - case features::UnretainedDanglingPtrMode::kDumpWithoutCrashing: - partition_alloc::SetUnretainedDanglingRawPtrDetectedFn( - &UnretainedDanglingRawPtrDetectedDumpWithoutCrashing); - break; - } -} - -namespace { - -#if BUILDFLAG(USE_STARSCAN) -void SetProcessNameForPCScan(const std::string& process_type) { - const char* name = [&process_type] { - if (process_type.empty()) { - // Empty means browser process. - return "Browser"; - } - if (process_type == switches::kRendererProcess) { - return "Renderer"; - } - if (process_type == switches::kGpuProcess) { - return "Gpu"; - } - if (process_type == switches::kUtilityProcess) { - return "Utility"; - } - return static_cast<const char*>(nullptr); - }(); - - if (name) { - partition_alloc::internal::PCScan::SetProcessName(name); - } -} - -bool EnablePCScanForMallocPartitionsIfNeeded() { -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - using Config = partition_alloc::internal::PCScan::InitConfig; - DCHECK(base::FeatureList::GetInstance()); - if (base::FeatureList::IsEnabled(base::features::kPartitionAllocPCScan)) { - allocator_shim::EnablePCScan({Config::WantedWriteProtectionMode::kEnabled, - Config::SafepointMode::kEnabled}); - base::allocator::RegisterPCScanStatsReporter(); - return true; - } -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - return false; -} - -bool EnablePCScanForMallocPartitionsInBrowserProcessIfNeeded() { -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - using Config = partition_alloc::internal::PCScan::InitConfig; - DCHECK(base::FeatureList::GetInstance()); - if (base::FeatureList::IsEnabled( - base::features::kPartitionAllocPCScanBrowserOnly)) { - const Config::WantedWriteProtectionMode wp_mode = - base::FeatureList::IsEnabled(base::features::kPartitionAllocDCScan) - ? Config::WantedWriteProtectionMode::kEnabled - : Config::WantedWriteProtectionMode::kDisabled; -#if !PA_CONFIG(STARSCAN_UFFD_WRITE_PROTECTOR_SUPPORTED) - CHECK_EQ(Config::WantedWriteProtectionMode::kDisabled, wp_mode) - << "DCScan is currently only supported on Linux based systems"; -#endif - allocator_shim::EnablePCScan({wp_mode, Config::SafepointMode::kEnabled}); - base::allocator::RegisterPCScanStatsReporter(); - return true; - } -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - return false; -} - -bool EnablePCScanForMallocPartitionsInRendererProcessIfNeeded() { -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - using Config = partition_alloc::internal::PCScan::InitConfig; - DCHECK(base::FeatureList::GetInstance()); - if (base::FeatureList::IsEnabled( - base::features::kPartitionAllocPCScanRendererOnly)) { - const Config::WantedWriteProtectionMode wp_mode = - base::FeatureList::IsEnabled(base::features::kPartitionAllocDCScan) - ? Config::WantedWriteProtectionMode::kEnabled - : Config::WantedWriteProtectionMode::kDisabled; -#if !PA_CONFIG(STARSCAN_UFFD_WRITE_PROTECTOR_SUPPORTED) - CHECK_EQ(Config::WantedWriteProtectionMode::kDisabled, wp_mode) - << "DCScan is currently only supported on Linux based systems"; -#endif - allocator_shim::EnablePCScan({wp_mode, Config::SafepointMode::kDisabled}); - base::allocator::RegisterPCScanStatsReporter(); - return true; - } -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - return false; -} -#endif // BUILDFLAG(USE_STARSCAN) - -} // namespace - -void ReconfigurePartitionForKnownProcess(const std::string& process_type) { - DCHECK_NE(process_type, switches::kZygoteProcess); - // TODO(keishi): Move the code to enable BRP back here after Finch - // experiments. -} - -PartitionAllocSupport* PartitionAllocSupport::Get() { - static auto* singleton = new PartitionAllocSupport(); - return singleton; -} - -PartitionAllocSupport::PartitionAllocSupport() = default; - -void PartitionAllocSupport::ReconfigureForTests() { - ReconfigureEarlyish(""); - base::AutoLock scoped_lock(lock_); - called_for_tests_ = true; -} - -// static -PartitionAllocSupport::BrpConfiguration -PartitionAllocSupport::GetBrpConfiguration(const std::string& process_type) { - // TODO(bartekn): Switch to DCHECK once confirmed there are no issues. - CHECK(base::FeatureList::GetInstance()); - - bool enable_brp = false; - bool enable_brp_zapping = false; - bool split_main_partition = false; - bool use_dedicated_aligned_partition = false; - bool add_dummy_ref_count = false; - bool process_affected_by_brp_flag = false; - bool enable_memory_reclaimer = false; - -#if (BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \ - BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)) || \ - BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) - if (base::FeatureList::IsEnabled( - base::features::kPartitionAllocBackupRefPtr)) { - // No specified process type means this is the Browser process. - switch (base::features::kBackupRefPtrEnabledProcessesParam.Get()) { - case base::features::BackupRefPtrEnabledProcesses::kBrowserOnly: - process_affected_by_brp_flag = process_type.empty(); - break; - case base::features::BackupRefPtrEnabledProcesses::kBrowserAndRenderer: - process_affected_by_brp_flag = - process_type.empty() || - (process_type == switches::kRendererProcess); - break; - case base::features::BackupRefPtrEnabledProcesses::kNonRenderer: - process_affected_by_brp_flag = - (process_type != switches::kRendererProcess); - break; - case base::features::BackupRefPtrEnabledProcesses::kAllProcesses: - process_affected_by_brp_flag = true; - break; - } - } -#endif // (BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && - // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)) || - // BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \ - BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - if (process_affected_by_brp_flag) { - switch (base::features::kBackupRefPtrModeParam.Get()) { - case base::features::BackupRefPtrMode::kDisabled: - // Do nothing. Equivalent to !IsEnabled(kPartitionAllocBackupRefPtr). - break; - - case base::features::BackupRefPtrMode::kEnabledWithMemoryReclaimer: - enable_memory_reclaimer = true; - ABSL_FALLTHROUGH_INTENDED; - case base::features::BackupRefPtrMode::kEnabled: - enable_brp_zapping = true; - ABSL_FALLTHROUGH_INTENDED; - case base::features::BackupRefPtrMode::kEnabledWithoutZapping: - enable_brp = true; - split_main_partition = true; -#if !BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) - // AlignedAlloc relies on natural alignment offered by the allocator - // (see the comment inside PartitionRoot::AlignedAllocFlags). Any extras - // in front of the allocation will mess up that alignment. Such extras - // are used when BackupRefPtr is on, in which case, we need a separate - // partition, dedicated to handle only aligned allocations, where those - // extras are disabled. However, if the "previous slot" variant is used, - // no dedicated partition is needed, as the extras won't interfere with - // the alignment requirements. - use_dedicated_aligned_partition = true; -#endif - break; - - case base::features::BackupRefPtrMode::kDisabledButSplitPartitions2Way: - split_main_partition = true; - break; - - case base::features::BackupRefPtrMode:: - kDisabledButSplitPartitions2WayWithMemoryReclaimer: - split_main_partition = true; - enable_memory_reclaimer = true; - break; - - case base::features::BackupRefPtrMode::kDisabledButSplitPartitions3Way: - split_main_partition = true; - use_dedicated_aligned_partition = true; - break; - - case base::features::BackupRefPtrMode::kDisabledButAddDummyRefCount: - split_main_partition = true; - add_dummy_ref_count = true; -#if !BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) - use_dedicated_aligned_partition = true; -#endif - break; - } - } -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && - // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - - return {enable_brp, - enable_brp_zapping, - enable_memory_reclaimer, - split_main_partition, - use_dedicated_aligned_partition, - add_dummy_ref_count, - process_affected_by_brp_flag}; -} - -void PartitionAllocSupport::ReconfigureEarlyish( - const std::string& process_type) { - { - base::AutoLock scoped_lock(lock_); - - // In tests, ReconfigureEarlyish() is called by ReconfigureForTest(), which - // is earlier than ContentMain(). - if (called_for_tests_) { - DCHECK(called_earlyish_); - return; - } - - // TODO(bartekn): Switch to DCHECK once confirmed there are no issues. - CHECK(!called_earlyish_) - << "ReconfigureEarlyish was already called for process '" - << established_process_type_ << "'; current process: '" << process_type - << "'"; - - called_earlyish_ = true; - established_process_type_ = process_type; - } - - if (process_type != switches::kZygoteProcess) { - ReconfigurePartitionForKnownProcess(process_type); - } - - // These initializations are only relevant for PartitionAlloc-Everywhere - // builds. -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - allocator_shim::EnablePartitionAllocMemoryReclaimer(); -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -} - -void PartitionAllocSupport::ReconfigureAfterZygoteFork( - const std::string& process_type) { - { - base::AutoLock scoped_lock(lock_); - // TODO(bartekn): Switch to DCHECK once confirmed there are no issues. - CHECK(!called_after_zygote_fork_) - << "ReconfigureAfterZygoteFork was already called for process '" - << established_process_type_ << "'; current process: '" << process_type - << "'"; - DCHECK(called_earlyish_) - << "Attempt to call ReconfigureAfterZygoteFork without calling " - "ReconfigureEarlyish; current process: '" - << process_type << "'"; - DCHECK_EQ(established_process_type_, switches::kZygoteProcess) - << "Attempt to call ReconfigureAfterZygoteFork while " - "ReconfigureEarlyish was called on non-zygote process '" - << established_process_type_ << "'; current process: '" << process_type - << "'"; - - called_after_zygote_fork_ = true; - established_process_type_ = process_type; - } - - if (process_type != switches::kZygoteProcess) { - ReconfigurePartitionForKnownProcess(process_type); - } -} - -void PartitionAllocSupport::ReconfigureAfterFeatureListInit( - const std::string& process_type, - bool configure_dangling_pointer_detector) { - if (configure_dangling_pointer_detector) { - base::allocator::InstallDanglingRawPtrChecks(); - } - base::allocator::InstallUnretainedDanglingRawPtrChecks(); - { - base::AutoLock scoped_lock(lock_); - // Avoid initializing more than once. - // TODO(bartekn): See if can be converted to (D)CHECK. - if (called_after_feature_list_init_) { - DCHECK_EQ(established_process_type_, process_type) - << "ReconfigureAfterFeatureListInit was already called for process '" - << established_process_type_ << "'; current process: '" - << process_type << "'"; - return; - } - DCHECK(called_earlyish_) - << "Attempt to call ReconfigureAfterFeatureListInit without calling " - "ReconfigureEarlyish; current process: '" - << process_type << "'"; - DCHECK_NE(established_process_type_, switches::kZygoteProcess) - << "Attempt to call ReconfigureAfterFeatureListInit without calling " - "ReconfigureAfterZygoteFork; current process: '" - << process_type << "'"; - DCHECK_EQ(established_process_type_, process_type) - << "ReconfigureAfterFeatureListInit wasn't called for an already " - "established process '" - << established_process_type_ << "'; current process: '" << process_type - << "'"; - - called_after_feature_list_init_ = true; - } - - DCHECK_NE(process_type, switches::kZygoteProcess); - [[maybe_unused]] BrpConfiguration brp_config = - GetBrpConfiguration(process_type); - -#if BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) - if (brp_config.process_affected_by_brp_flag) { - base::RawPtrAsanService::GetInstance().Configure( - base::EnableDereferenceCheck( - base::features::kBackupRefPtrAsanEnableDereferenceCheckParam.Get()), - base::EnableExtractionCheck( - base::features::kBackupRefPtrAsanEnableExtractionCheckParam.Get()), - base::EnableInstantiationCheck( - base::features::kBackupRefPtrAsanEnableInstantiationCheckParam - .Get())); - } else { - base::RawPtrAsanService::GetInstance().Configure( - base::EnableDereferenceCheck(false), base::EnableExtractionCheck(false), - base::EnableInstantiationCheck(false)); - } -#endif // BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - // No specified type means we are in the browser. - auto bucket_distribution = - process_type == "" - ? base::features::kPartitionAllocAlternateBucketDistributionParam - .Get() - : base::features::AlternateBucketDistributionMode::kDefault; - - allocator_shim::ConfigurePartitions( - allocator_shim::EnableBrp(brp_config.enable_brp), - allocator_shim::EnableBrpZapping(brp_config.enable_brp_zapping), - allocator_shim::EnableBrpPartitionMemoryReclaimer( - brp_config.enable_brp_partition_memory_reclaimer), - allocator_shim::SplitMainPartition(brp_config.split_main_partition), - allocator_shim::UseDedicatedAlignedPartition( - brp_config.use_dedicated_aligned_partition), - allocator_shim::AddDummyRefCount(brp_config.add_dummy_ref_count), - allocator_shim::AlternateBucketDistribution(bucket_distribution)); -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - - // If BRP is not enabled, check if any of PCScan flags is enabled. - [[maybe_unused]] bool scan_enabled = false; -#if BUILDFLAG(USE_STARSCAN) - if (!brp_config.enable_brp) { - scan_enabled = EnablePCScanForMallocPartitionsIfNeeded(); - // No specified process type means this is the Browser process. - if (process_type.empty()) { - scan_enabled = scan_enabled || - EnablePCScanForMallocPartitionsInBrowserProcessIfNeeded(); - } - if (process_type == switches::kRendererProcess) { - scan_enabled = scan_enabled || - EnablePCScanForMallocPartitionsInRendererProcessIfNeeded(); - } - if (scan_enabled) { - if (base::FeatureList::IsEnabled( - base::features::kPartitionAllocPCScanStackScanning)) { -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - partition_alloc::internal::PCScan::EnableStackScanning(); - // Notify PCScan about the main thread. - partition_alloc::internal::PCScan::NotifyThreadCreated( - partition_alloc::internal::GetStackTop()); -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - } - if (base::FeatureList::IsEnabled( - base::features::kPartitionAllocPCScanImmediateFreeing)) { - partition_alloc::internal::PCScan::EnableImmediateFreeing(); - } - if (base::FeatureList::IsEnabled( - base::features::kPartitionAllocPCScanEagerClearing)) { - partition_alloc::internal::PCScan::SetClearType( - partition_alloc::internal::PCScan::ClearType::kEager); - } - SetProcessNameForPCScan(process_type); - } - } -#endif // BUILDFLAG(USE_STARSCAN) - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -#if BUILDFLAG(USE_STARSCAN) - // Non-quarantinable partition is dealing with hot V8's zone allocations. - // In case PCScan is enabled in Renderer, enable thread cache on this - // partition. At the same time, thread cache on the main(malloc) partition - // must be disabled, because only one partition can have it on. - if (scan_enabled && process_type == switches::kRendererProcess) { - base::internal::NonQuarantinableAllocator::Instance() - .root() - ->EnableThreadCacheIfSupported(); - } else -#endif // BUILDFLAG(USE_STARSCAN) - { - allocator_shim::internal::PartitionAllocMalloc::Allocator() - ->EnableThreadCacheIfSupported(); - } - - if (base::FeatureList::IsEnabled( - base::features::kPartitionAllocLargeEmptySlotSpanRing)) { - allocator_shim::internal::PartitionAllocMalloc::Allocator() - ->EnableLargeEmptySlotSpanRing(); - allocator_shim::internal::PartitionAllocMalloc::AlignedAllocator() - ->EnableLargeEmptySlotSpanRing(); - } -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - -#if BUILDFLAG(IS_WIN) - // Browser process only, since this is the one we want to prevent from - // crashing the most (as it takes down all the tabs). - if (base::FeatureList::IsEnabled( - base::features::kPageAllocatorRetryOnCommitFailure) && - process_type.empty()) { - partition_alloc::SetRetryOnCommitFailure(true); - } -#endif -} - -void PartitionAllocSupport::ReconfigureAfterTaskRunnerInit( - const std::string& process_type) { - { - base::AutoLock scoped_lock(lock_); - - // Init only once. - if (called_after_thread_pool_init_) { - return; - } - - DCHECK_EQ(established_process_type_, process_type); - // Enforce ordering. - DCHECK(called_earlyish_); - DCHECK(called_after_feature_list_init_); - - called_after_thread_pool_init_ = true; - } - -#if PA_CONFIG(THREAD_CACHE_SUPPORTED) && \ - BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - // This should be called in specific processes, as the main thread is - // initialized later. - DCHECK(process_type != switches::kZygoteProcess); - - base::allocator::StartThreadCachePeriodicPurge(); - -#if BUILDFLAG(IS_ANDROID) - // Lower thread cache limits to avoid stranding too much memory in the caches. - if (base::SysInfo::IsLowEndDeviceOrPartialLowEndModeEnabled()) { - ::partition_alloc::ThreadCacheRegistry::Instance().SetThreadCacheMultiplier( - ::partition_alloc::ThreadCache::kDefaultMultiplier / 2.); - } -#endif // BUILDFLAG(IS_ANDROID) - - // Renderer processes are more performance-sensitive, increase thread cache - // limits. - if (process_type == switches::kRendererProcess && - base::FeatureList::IsEnabled( - base::features::kPartitionAllocLargeThreadCacheSize)) { - largest_cached_size_ = - ::partition_alloc::ThreadCacheLimits::kLargeSizeThreshold; - -#if BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_32_BITS) - // Devices almost always report less physical memory than what they actually - // have, so anything above 3GiB will catch 4GiB and above. - if (base::SysInfo::AmountOfPhysicalMemoryMB() <= 3500) { - largest_cached_size_ = - ::partition_alloc::ThreadCacheLimits::kDefaultSizeThreshold; - } -#endif // BUILDFLAG(IS_ANDROID) && !defined(ARCH_CPU_64_BITS) - - ::partition_alloc::ThreadCache::SetLargestCachedSize(largest_cached_size_); - } -#endif // PA_CONFIG(THREAD_CACHE_SUPPORTED) && - // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - -#if BUILDFLAG(USE_STARSCAN) - if (base::FeatureList::IsEnabled( - base::features::kPartitionAllocPCScanMUAwareScheduler)) { - // Assign PCScan a task-based scheduling backend. - static base::NoDestructor< - partition_alloc::internal::MUAwareTaskBasedBackend> - mu_aware_task_based_backend{ - partition_alloc::internal::PCScan::scheduler(), - &partition_alloc::internal::PCScan::PerformDelayedScan}; - partition_alloc::internal::PCScan::scheduler().SetNewSchedulingBackend( - *mu_aware_task_based_backend.get()); - } -#endif // BUILDFLAG(USE_STARSCAN) - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - base::allocator::StartMemoryReclaimer( - base::SingleThreadTaskRunner::GetCurrentDefault()); -#endif - - if (base::FeatureList::IsEnabled( - base::features::kPartitionAllocSortActiveSlotSpans)) { - partition_alloc::PartitionRoot< - partition_alloc::internal::ThreadSafe>::EnableSortActiveSlotSpans(); - } -} - -void PartitionAllocSupport::OnForegrounded(bool has_main_frame) { -#if PA_CONFIG(THREAD_CACHE_SUPPORTED) && \ - BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - { - base::AutoLock scoped_lock(lock_); - if (established_process_type_ != switches::kRendererProcess) { - return; - } - } - - if (!base::FeatureList::IsEnabled( - features::kLowerPAMemoryLimitForNonMainRenderers) || - has_main_frame) { - ::partition_alloc::ThreadCache::SetLargestCachedSize(largest_cached_size_); - } -#endif // PA_CONFIG(THREAD_CACHE_SUPPORTED) && - // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -} - -void PartitionAllocSupport::OnBackgrounded() { -#if PA_CONFIG(THREAD_CACHE_SUPPORTED) && \ - BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - { - base::AutoLock scoped_lock(lock_); - if (established_process_type_ != switches::kRendererProcess) { - return; - } - } - - // Performance matters less for background renderers, don't pay the memory - // cost. - ::partition_alloc::ThreadCache::SetLargestCachedSize( - ::partition_alloc::ThreadCacheLimits::kDefaultSizeThreshold); - - // In renderers, memory reclaim uses the "idle time" task runner to run - // periodic reclaim. This does not always run when the renderer is idle, and - // in particular after the renderer gets backgrounded. As a result, empty slot - // spans are potentially never decommitted. To mitigate that, run a one-off - // reclaim a few seconds later. Even if the renderer comes back to foreground - // in the meantime, the worst case is a few more system calls. - // - // TODO(lizeb): Remove once/if the behavior of idle tasks changes. - base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask( - FROM_HERE, base::BindOnce([]() { - ::partition_alloc::MemoryReclaimer::Instance()->ReclaimAll(); - }), - base::Seconds(10)); - -#endif // PA_CONFIG(THREAD_CACHE_SUPPORTED) && - // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -} - -#if BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) -std::string PartitionAllocSupport::ExtractDanglingPtrSignatureForTests( - std::string stacktrace) { - return ExtractDanglingPtrSignature(stacktrace); -} -#endif - -} // namespace base::allocator
diff --git a/base/allocator/partition_alloc_support.h b/base/allocator/partition_alloc_support.h deleted file mode 100644 index f9095a3..0000000 --- a/base/allocator/partition_alloc_support.h +++ /dev/null
@@ -1,119 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOC_SUPPORT_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOC_SUPPORT_H_ - -#include <map> -#include <string> - -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_config.h" -#include "base/allocator/partition_allocator/thread_cache.h" -#include "base/base_export.h" -#include "base/memory/scoped_refptr.h" -#include "base/synchronization/lock.h" -#include "base/task/sequenced_task_runner.h" -#include "base/thread_annotations.h" - -namespace base::allocator { - -#if BUILDFLAG(USE_STARSCAN) -BASE_EXPORT void RegisterPCScanStatsReporter(); -#endif - -// Starts a periodic timer on the current thread to purge all thread caches. -BASE_EXPORT void StartThreadCachePeriodicPurge(); - -BASE_EXPORT void StartMemoryReclaimer( - scoped_refptr<SequencedTaskRunner> task_runner); - -BASE_EXPORT std::map<std::string, std::string> ProposeSyntheticFinchTrials(); - -// Install handlers for when dangling raw_ptr(s) have been detected. This prints -// two StackTraces. One where the memory is freed, one where the last dangling -// raw_ptr stopped referencing it. -// -// This is currently effective, only when compiled with -// `enable_dangling_raw_ptr_checks` build flag. -BASE_EXPORT void InstallDanglingRawPtrChecks(); -BASE_EXPORT void InstallUnretainedDanglingRawPtrChecks(); - -// Allows to re-configure PartitionAlloc at run-time. -class BASE_EXPORT PartitionAllocSupport { - public: - struct BrpConfiguration { - bool enable_brp = false; - bool enable_brp_zapping = false; - bool enable_brp_partition_memory_reclaimer = false; - bool split_main_partition = false; - bool use_dedicated_aligned_partition = false; - bool add_dummy_ref_count = false; - bool process_affected_by_brp_flag = false; - }; - // Reconfigure* functions re-configure PartitionAlloc. It is impossible to - // configure PartitionAlloc before/at its initialization using information not - // known at compile-time (e.g. process type, Finch), because by the time this - // information is available memory allocations would have surely happened, - // that requiring a functioning allocator. - // - // *Earlyish() is called as early as it is reasonably possible. - // *AfterZygoteFork() is its complement to finish configuring process-specific - // stuff that had to be postponed due to *Earlyish() being called with - // |process_type==kZygoteProcess|. - // *AfterFeatureListInit() is called in addition to the above, once - // FeatureList has been initialized and ready to use. It is guaranteed to be - // called on non-zygote processes or after the zygote has been forked. - // *AfterTaskRunnerInit() is called once it is possible to post tasks, and - // after the previous steps. - // - // *Earlyish() must be called exactly once. *AfterZygoteFork() must be called - // once iff *Earlyish() was called before with |process_type==kZygoteProcess|. - // - // *AfterFeatureListInit() may be called more than once, but will perform its - // re-configuration steps exactly once. - // - // *AfterTaskRunnerInit() may be called more than once. - void ReconfigureForTests(); - void ReconfigureEarlyish(const std::string& process_type); - void ReconfigureAfterZygoteFork(const std::string& process_type); - void ReconfigureAfterFeatureListInit( - const std::string& process_type, - bool configure_dangling_pointer_detector = true); - void ReconfigureAfterTaskRunnerInit(const std::string& process_type); - - // |has_main_frame| tells us if the renderer contains a main frame. - void OnForegrounded(bool has_main_frame); - void OnBackgrounded(); - -#if BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) - static std::string ExtractDanglingPtrSignatureForTests( - std::string stacktrace); -#endif - - static PartitionAllocSupport* Get(); - - static BrpConfiguration GetBrpConfiguration(const std::string& process_type); - - private: - PartitionAllocSupport(); - - base::Lock lock_; - bool called_for_tests_ GUARDED_BY(lock_) = false; - bool called_earlyish_ GUARDED_BY(lock_) = false; - bool called_after_zygote_fork_ GUARDED_BY(lock_) = false; - bool called_after_feature_list_init_ GUARDED_BY(lock_) = false; - bool called_after_thread_pool_init_ GUARDED_BY(lock_) = false; - std::string established_process_type_ GUARDED_BY(lock_) = "INVALID"; - -#if PA_CONFIG(THREAD_CACHE_SUPPORTED) && \ - BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - size_t largest_cached_size_ = - ::partition_alloc::ThreadCacheLimits::kDefaultSizeThreshold; -#endif -}; - -} // namespace base::allocator - -#endif // BASE_ALLOCATOR_PARTITION_ALLOC_SUPPORT_H_
diff --git a/base/allocator/partition_alloc_support_unittest.cc b/base/allocator/partition_alloc_support_unittest.cc deleted file mode 100644 index 13a6da5..0000000 --- a/base/allocator/partition_alloc_support_unittest.cc +++ /dev/null
@@ -1,385 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_alloc_support.h" - -#include <string> -#include <utility> -#include <vector> - -#include "base/allocator/partition_alloc_features.h" -#include "base/allocator/partition_allocator/dangling_raw_ptr_checks.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/feature_list.h" -#include "base/task/single_thread_task_runner.h" -#include "base/test/gtest_util.h" -#include "base/test/scoped_feature_list.h" -#include "base/test/task_environment.h" -#include "build/build_config.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace allocator { - -using testing::AllOf; -using testing::HasSubstr; - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -TEST(PartitionAllocSupportTest, ProposeSyntheticFinchTrials_BRPAndPCScan) { - for (bool pcscan_enabled : {false, true}) { - test::ScopedFeatureList pcscan_scope; - std::vector<test::FeatureRef> empty_list = {}; - std::vector<test::FeatureRef> pcscan_list = { - features::kPartitionAllocPCScanBrowserOnly}; - pcscan_scope.InitWithFeatures(pcscan_enabled ? pcscan_list : empty_list, - pcscan_enabled ? empty_list : pcscan_list); -#if !BUILDFLAG(USE_STARSCAN) - pcscan_enabled = false; -#endif - - std::string brp_expectation; - std::string pcscan_expectation; - - { - test::ScopedFeatureList brp_scope; - brp_scope.InitWithFeatures({}, {features::kPartitionAllocBackupRefPtr}); - - brp_expectation = "Unavailable"; -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - brp_expectation = pcscan_enabled ? "Ignore_PCScanIsOn" : "Ignore_NoGroup"; -#endif - pcscan_expectation = "Unavailable"; -#if BUILDFLAG(USE_STARSCAN) - pcscan_expectation = pcscan_enabled ? "Enabled" : "Disabled"; -#endif - - auto trials = ProposeSyntheticFinchTrials(); - auto group_iter = trials.find("BackupRefPtr_Effective"); - EXPECT_NE(group_iter, trials.end()); - EXPECT_EQ(group_iter->second, brp_expectation); - group_iter = trials.find("PCScan_Effective"); - EXPECT_NE(group_iter, trials.end()); - EXPECT_EQ(group_iter->second, pcscan_expectation); - group_iter = trials.find("PCScan_Effective_Fallback"); - EXPECT_NE(group_iter, trials.end()); - EXPECT_EQ(group_iter->second, pcscan_expectation); - } - - { - test::ScopedFeatureList brp_scope; - brp_scope.InitAndEnableFeatureWithParameters( - features::kPartitionAllocBackupRefPtr, {}); - - brp_expectation = "Unavailable"; -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - brp_expectation = pcscan_enabled ? "Ignore_PCScanIsOn" -#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN) || \ - (BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) && BUILDFLAG(IS_LINUX)) || \ - BUILDFLAG(ENABLE_BACKUP_REF_PTR_FEATURE_FLAG) -#if BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) - : "EnabledPrevSlot_NonRenderer"; -#else - : "EnabledBeforeAlloc_NonRenderer"; -#endif // BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) -#else -#if BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) - : "EnabledPrevSlot_BrowserOnly"; -#else - : "EnabledBeforeAlloc_BrowserOnly"; -#endif // BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) -#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN) || - // (BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) && BUILDFLAG(IS_LINUX)) -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - pcscan_expectation = "Unavailable"; -#if BUILDFLAG(USE_STARSCAN) -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - pcscan_expectation = "Ignore_BRPIsOn"; -#else - pcscan_expectation = pcscan_enabled ? "Enabled" : "Disabled"; -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) -#endif // BUILDFLAG(USE_STARSCAN) - - auto trials = ProposeSyntheticFinchTrials(); - auto group_iter = trials.find("BackupRefPtr_Effective"); - EXPECT_NE(group_iter, trials.end()); - EXPECT_EQ(group_iter->second, brp_expectation); - group_iter = trials.find("PCScan_Effective"); - EXPECT_NE(group_iter, trials.end()); - EXPECT_EQ(group_iter->second, pcscan_expectation); - group_iter = trials.find("PCScan_Effective_Fallback"); - EXPECT_NE(group_iter, trials.end()); - EXPECT_EQ(group_iter->second, pcscan_expectation); - } - - const std::string kEnabledMode = -#if BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) - "PrevSlot_"; -#else - "BeforeAlloc_"; -#endif - const std::vector<std::pair<std::string, std::string>> kModes = { - {"disabled", "Disabled"}, - {"enabled", "Enabled" + kEnabledMode}, - {"disabled-but-2-way-split", "DisabledBut2WaySplit_"}, - {"disabled-but-3-way-split", "DisabledBut3WaySplit_"}}; - const std::vector<std::pair<std::string, std::string>> kProcesses = { - {"browser-only", "BrowserOnly"}, - {"browser-and-renderer", "BrowserAndRenderer"}, - {"non-renderer", "NonRenderer"}, - {"all-processes", "AllProcesses"}}; - - for (auto mode : kModes) { - for (auto process_set : kProcesses) { - test::ScopedFeatureList brp_scope; - brp_scope.InitAndEnableFeatureWithParameters( - features::kPartitionAllocBackupRefPtr, - {{"brp-mode", mode.first}, - {"enabled-processes", process_set.first}}); - - [[maybe_unused]] bool brp_truly_enabled = false; - [[maybe_unused]] bool brp_nondefault_behavior = false; - brp_expectation = "Unavailable"; -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - brp_expectation = pcscan_enabled ? "Ignore_PCScanIsOn" : mode.second; - brp_truly_enabled = (mode.first == "enabled"); - brp_nondefault_behavior = (mode.first != "disabled"); -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - if (brp_expectation[brp_expectation.length() - 1] == '_') { - brp_expectation += process_set.second; - } - pcscan_expectation = "Unavailable"; - std::string pcscan_expectation_fallback = "Unavailable"; -#if BUILDFLAG(USE_STARSCAN) - pcscan_expectation = brp_truly_enabled - ? "Ignore_BRPIsOn" - : (pcscan_enabled ? "Enabled" : "Disabled"); - pcscan_expectation_fallback = - brp_nondefault_behavior ? "Ignore_BRPIsOn" - : (pcscan_enabled ? "Enabled" : "Disabled"); -#endif // BUILDFLAG(USE_STARSCAN) - - auto trials = ProposeSyntheticFinchTrials(); - auto group_iter = trials.find("BackupRefPtr_Effective"); - EXPECT_NE(group_iter, trials.end()); - EXPECT_EQ(group_iter->second, brp_expectation); - group_iter = trials.find("PCScan_Effective"); - EXPECT_NE(group_iter, trials.end()); - EXPECT_EQ(group_iter->second, pcscan_expectation); - group_iter = trials.find("PCScan_Effective_Fallback"); - EXPECT_NE(group_iter, trials.end()); - EXPECT_EQ(group_iter->second, pcscan_expectation_fallback); - } - } - } -} -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - -TEST(PartitionAllocSupportTest, - ProposeSyntheticFinchTrials_DanglingPointerDetector) { - std::string dpd_group = - ProposeSyntheticFinchTrials()["DanglingPointerDetector"]; - -#if BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) - EXPECT_EQ(dpd_group, "Enabled"); -#else - EXPECT_EQ(dpd_group, "Disabled"); -#endif -} - -// - Death tests misbehave on Android, http://crbug.com/643760. -#if BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) && !BUILDFLAG(IS_ANDROID) && \ - defined(GTEST_HAS_DEATH_TEST) - -namespace { - -// Install dangling raw_ptr handler and restore them when going out of scope. -class ScopedInstallDanglingRawPtrChecks { - public: - struct ConstructorParams { - std::string mode = "crash"; - std::string type = "all"; - }; - ScopedInstallDanglingRawPtrChecks(ConstructorParams params) { - enabled_feature_list_.InitWithFeaturesAndParameters( - {{features::kPartitionAllocDanglingPtr, - {{"mode", params.mode}, {"type", params.type}}}}, - {/* disabled_features */}); - - old_detected_fn_ = partition_alloc::GetDanglingRawPtrDetectedFn(); - old_dereferenced_fn_ = partition_alloc::GetDanglingRawPtrReleasedFn(); - InstallDanglingRawPtrChecks(); - } - ScopedInstallDanglingRawPtrChecks() - : ScopedInstallDanglingRawPtrChecks(ConstructorParams{}) {} - ~ScopedInstallDanglingRawPtrChecks() { - partition_alloc::SetDanglingRawPtrDetectedFn(old_detected_fn_); - partition_alloc::SetDanglingRawPtrReleasedFn(old_dereferenced_fn_); - } - - private: - test::ScopedFeatureList enabled_feature_list_; - partition_alloc::DanglingRawPtrDetectedFn* old_detected_fn_; - partition_alloc::DanglingRawPtrReleasedFn* old_dereferenced_fn_; -}; - -} // namespace - -TEST(PartitionAllocDanglingPtrChecks, Basic) { - ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks; - partition_alloc::GetDanglingRawPtrDetectedFn()(42); - EXPECT_DEATH( - partition_alloc::GetDanglingRawPtrReleasedFn()(42), - AllOf(HasSubstr("Detected dangling raw_ptr with id=0x000000000000002a:"), - HasSubstr("[DanglingSignature]\t"), - HasSubstr("The memory was freed at:"), - HasSubstr("The dangling raw_ptr was released at:"))); -} - -// The StackTrace buffer might run out of storage and not record where the -// memory was freed. Anyway, it must still report the error. -TEST(PartitionAllocDanglingPtrChecks, FreeNotRecorded) { - ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks; - EXPECT_DEATH( - partition_alloc::GetDanglingRawPtrReleasedFn()(42), - AllOf(HasSubstr("Detected dangling raw_ptr with id=0x000000000000002a:"), - HasSubstr("[DanglingSignature]\tmissing\tmissing\t"), - HasSubstr("It was not recorded where the memory was freed."), - HasSubstr("The dangling raw_ptr was released at:"))); -} - -// DCHECK message are stripped in official build. It causes death tests with -// matchers to fail. -#if !defined(OFFICIAL_BUILD) || !defined(NDEBUG) -TEST(PartitionAllocDanglingPtrChecks, DoubleDetection) { - ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks; - partition_alloc::GetDanglingRawPtrDetectedFn()(42); - EXPECT_DCHECK_DEATH_WITH(partition_alloc::GetDanglingRawPtrDetectedFn()(42), - "Check failed: !entry \\|\\| entry->id != id"); -} -#endif // !defined(OFFICIAL_BUILD) || !defined(NDEBUG) - -// Free and release from two different tasks with cross task dangling pointer -// detection enabled. -TEST(PartitionAllocDanglingPtrChecks, CrossTask) { - ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks({ - .type = "cross_task", - }); - - base::test::TaskEnvironment task_environment; - task_environment.GetMainThreadTaskRunner()->PostTask( - FROM_HERE, - base::BindOnce(partition_alloc::GetDanglingRawPtrDetectedFn(), 42)); - task_environment.GetMainThreadTaskRunner()->PostTask( - FROM_HERE, base::BindOnce([]() { - BASE_EXPECT_DEATH( - partition_alloc::GetDanglingRawPtrReleasedFn()(42), - AllOf(HasSubstr( - "Detected dangling raw_ptr with id=0x000000000000002a:"), - HasSubstr("[DanglingSignature]\t"), - HasSubstr("The memory was freed at:"), - HasSubstr("The dangling raw_ptr was released at:"))); - })); - task_environment.RunUntilIdle(); -} - -TEST(PartitionAllocDanglingPtrChecks, CrossTaskIgnoredFailuresClearsCache) { - ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks({ - .type = "cross_task", - }); - - base::test::TaskEnvironment task_environment; - partition_alloc::GetDanglingRawPtrDetectedFn()(42); - partition_alloc::GetDanglingRawPtrReleasedFn()(42); - task_environment.GetMainThreadTaskRunner()->PostTask( - FROM_HERE, - base::BindOnce(partition_alloc::GetDanglingRawPtrReleasedFn(), 42)); - task_environment.RunUntilIdle(); -} - -TEST(PartitionAllocDanglingPtrChecks, CrossTaskIgnoresNoTask) { - ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks({ - .type = "cross_task", - }); - - partition_alloc::GetDanglingRawPtrDetectedFn()(42); - partition_alloc::GetDanglingRawPtrReleasedFn()(42); -} - -TEST(PartitionAllocDanglingPtrChecks, CrossTaskIgnoresSameTask) { - ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks({ - .type = "cross_task", - }); - - base::test::TaskEnvironment task_environment; - task_environment.GetMainThreadTaskRunner()->PostTask( - FROM_HERE, base::BindOnce([]() { - partition_alloc::GetDanglingRawPtrDetectedFn()(37); - partition_alloc::GetDanglingRawPtrReleasedFn()(37); - })); - task_environment.RunUntilIdle(); -} - -TEST(PartitionAllocDanglingPtrChecks, CrossTaskNoFreeConsideredCrossTask) { - ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks({ - .type = "cross_task", - }); - partition_alloc::GetDanglingRawPtrReleasedFn()(42); -} - -TEST(PartitionAllocDanglingPtrChecks, - ExtractDanglingPtrSignatureMacStackTrace) { - const std::string stack_trace_output = - "0 lib_1 0x0000000115fdfa12 base::F1(**) + 18\r\n" - "1 lib_1 0x0000000115ec0043 base::F2() + 19\r\n" - "2 lib_1 0x000000011601fb01 " - "allocator_shim::internal::PartitionFree(foo) + 13265\r\n" - "3 lib_1 0x0000000114831027 base::F3(bar) + 42\r\n" - "4 lib_2 0x00000001148eae35 base::F4() + 437\r\n"; - EXPECT_EQ("base::F3(bar)", - PartitionAllocSupport::ExtractDanglingPtrSignatureForTests( - stack_trace_output)); -} - -TEST(PartitionAllocDanglingPtrChecks, ExtractDanglingPtrSignatureMacTaskTrace) { - const std::string task_trace_output = - "Task trace:\r\n" - "0 lib_1 0x00000001161fd431 base::F1() + 257\r\n" - "1 lib_1 0x0000000115a49404 base::F2() + 68\r\n"; - EXPECT_EQ("base::F1()", - PartitionAllocSupport::ExtractDanglingPtrSignatureForTests( - task_trace_output)); -} - -TEST(PartitionAllocDanglingPtrChecks, - ExtractDanglingPtrSignatureWindowsStackTrace) { - const std::string stack_trace_output = - "Backtrace:\r\n" - "\tbase::F1 [0x055643C3+19] (o:\\base\\F1.cc:329)\r\n" - "\tallocator_shim::internal::PartitionFree [0x0648F87B+5243] " - "(o:\\path.cc:441)\r\n" - "\t_free_base [0x0558475D+29] (o:\\file_path.cc:142)\r\n" - "\tbase::F2 [0x04E5B317+23] (o:\\base\\F2.cc:91)\r\n" - "\tbase::F3 [0x04897800+544] (o:\\base\\F3.cc:638)\r\n"; - EXPECT_EQ("base::F2", - PartitionAllocSupport::ExtractDanglingPtrSignatureForTests( - stack_trace_output)); -} - -TEST(PartitionAllocDanglingPtrChecks, - ExtractDanglingPtrSignatureWindowsTaskTrace) { - const std::string task_trace_output = - "Task trace:\r\n" - "Backtrace:\r\n" - "\tbase::F1 [0x049068A3+813] (o:\\base\\F1.cc:207)\r\n" - "\tbase::F2 [0x0490614C+192] (o:\\base\\F2.cc:116)\r\n"; - EXPECT_EQ("base::F1", - PartitionAllocSupport::ExtractDanglingPtrSignatureForTests( - task_trace_output)); -} - -#endif - -} // namespace allocator -} // namespace base
diff --git a/base/allocator/partition_allocator/.gn b/base/allocator/partition_allocator/.gn deleted file mode 100644 index ee3f918..0000000 --- a/base/allocator/partition_allocator/.gn +++ /dev/null
@@ -1,10 +0,0 @@ -# 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. - -# The python interpreter to use by default. On Windows, this will look -# for python3.exe and python3.bat. -script_executable = "python3" - -# The location of the build configuration file. -buildconfig = "//build/config/BUILDCONFIG.gn"
diff --git a/base/allocator/partition_allocator/BUILD.gn b/base/allocator/partition_allocator/BUILD.gn deleted file mode 100644 index 910b4da..0000000 --- a/base/allocator/partition_allocator/BUILD.gn +++ /dev/null
@@ -1,517 +0,0 @@ -# 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("//base/allocator/partition_allocator/partition_alloc.gni") -import("//build/buildflag_header.gni") -import("//build/config/chromecast_build.gni") -import("//build/config/chromeos/ui_mode.gni") -import("//build/config/dcheck_always_on.gni") -import("//build/config/logging.gni") - -# Add partition_alloc.gni and import it for partition_alloc configs. - -config("partition_alloc_implementation") { - # See also: `partition_alloc_base/component_export.h` - defines = [ "IS_PARTITION_ALLOC_IMPL" ] -} - -config("memory_tagging") { - if (current_cpu == "arm64" && is_clang && - (is_linux || is_chromeos || is_android || is_fuchsia)) { - # base/ has access to the MTE intrinsics because it needs to use them, - # but they're not backwards compatible. Use base::CPU::has_mte() - # beforehand to confirm or use indirect functions (ifuncs) to select - # an MTE-specific implementation at dynamic link-time. - cflags = [ - "-Xclang", - "-target-feature", - "-Xclang", - "+mte", - ] - } -} - -if (is_fuchsia) { - config("fuchsia_sync_lib") { - libs = [ - "sync", # Used by spinning_mutex.h. - ] - } -} - -if (enable_pkeys && is_debug) { - config("no_stack_protector") { - cflags = [ "-fno-stack-protector" ] - } -} - -component("partition_alloc") { - sources = [ - "address_pool_manager.cc", - "address_pool_manager.h", - "address_pool_manager_bitmap.cc", - "address_pool_manager_bitmap.h", - "address_pool_manager_types.h", - "address_space_randomization.cc", - "address_space_randomization.h", - "address_space_stats.h", - "allocation_guard.cc", - "allocation_guard.h", - "compressed_pointer.cc", - "compressed_pointer.h", - "dangling_raw_ptr_checks.cc", - "dangling_raw_ptr_checks.h", - "freeslot_bitmap.h", - "freeslot_bitmap_constants.h", - "gwp_asan_support.cc", - "gwp_asan_support.h", - "memory_reclaimer.cc", - "memory_reclaimer.h", - "oom.cc", - "oom.h", - "oom_callback.cc", - "oom_callback.h", - "page_allocator.cc", - "page_allocator.h", - "page_allocator_constants.h", - "page_allocator_internal.h", - "partition_address_space.cc", - "partition_address_space.h", - "partition_alloc-inl.h", - "partition_alloc.cc", - "partition_alloc.h", - "partition_alloc_base/atomic_ref_count.h", - "partition_alloc_base/augmentations/compiler_specific.h", - "partition_alloc_base/bit_cast.h", - "partition_alloc_base/bits.h", - "partition_alloc_base/check.cc", - "partition_alloc_base/check.h", - "partition_alloc_base/compiler_specific.h", - "partition_alloc_base/component_export.h", - "partition_alloc_base/cpu.cc", - "partition_alloc_base/cpu.h", - "partition_alloc_base/cxx17_backports.h", - "partition_alloc_base/cxx20_is_constant_evaluated.h", - "partition_alloc_base/debug/alias.cc", - "partition_alloc_base/debug/alias.h", - "partition_alloc_base/gtest_prod_util.h", - "partition_alloc_base/immediate_crash.h", - "partition_alloc_base/logging.cc", - "partition_alloc_base/logging.h", - "partition_alloc_base/memory/ref_counted.cc", - "partition_alloc_base/memory/ref_counted.h", - "partition_alloc_base/memory/scoped_policy.h", - "partition_alloc_base/memory/scoped_refptr.h", - "partition_alloc_base/no_destructor.h", - "partition_alloc_base/numerics/checked_math.h", - "partition_alloc_base/numerics/checked_math_impl.h", - "partition_alloc_base/numerics/clamped_math.h", - "partition_alloc_base/numerics/clamped_math_impl.h", - "partition_alloc_base/numerics/math_constants.h", - "partition_alloc_base/numerics/ostream_operators.h", - "partition_alloc_base/numerics/ranges.h", - "partition_alloc_base/numerics/safe_conversions.h", - "partition_alloc_base/numerics/safe_conversions_arm_impl.h", - "partition_alloc_base/numerics/safe_conversions_impl.h", - "partition_alloc_base/numerics/safe_math.h", - "partition_alloc_base/numerics/safe_math_arm_impl.h", - "partition_alloc_base/numerics/safe_math_clang_gcc_impl.h", - "partition_alloc_base/numerics/safe_math_shared_impl.h", - "partition_alloc_base/posix/eintr_wrapper.h", - "partition_alloc_base/rand_util.cc", - "partition_alloc_base/rand_util.h", - "partition_alloc_base/scoped_clear_last_error.h", - "partition_alloc_base/strings/stringprintf.cc", - "partition_alloc_base/strings/stringprintf.h", - "partition_alloc_base/system/sys_info.h", - "partition_alloc_base/thread_annotations.h", - "partition_alloc_base/threading/platform_thread.cc", - "partition_alloc_base/threading/platform_thread.h", - "partition_alloc_base/threading/platform_thread_ref.h", - "partition_alloc_base/time/time.cc", - "partition_alloc_base/time/time.h", - "partition_alloc_base/time/time_override.cc", - "partition_alloc_base/time/time_override.h", - "partition_alloc_base/types/strong_alias.h", - "partition_alloc_base/win/win_handle_types.h", - "partition_alloc_base/win/win_handle_types_list.inc", - "partition_alloc_base/win/windows_types.h", - "partition_alloc_check.h", - "partition_alloc_config.h", - "partition_alloc_constants.h", - "partition_alloc_forward.h", - "partition_alloc_hooks.cc", - "partition_alloc_hooks.h", - "partition_alloc_notreached.h", - "partition_bucket.cc", - "partition_bucket.h", - "partition_bucket_lookup.h", - "partition_cookie.h", - "partition_direct_map_extent.h", - "partition_freelist_entry.h", - "partition_lock.h", - "partition_oom.cc", - "partition_oom.h", - "partition_page.cc", - "partition_page.h", - "partition_ref_count.h", - "partition_root.cc", - "partition_root.h", - "partition_stats.cc", - "partition_stats.h", - "partition_tls.h", - "pkey.cc", - "pkey.h", - "random.cc", - "random.h", - "reservation_offset_table.cc", - "reservation_offset_table.h", - "reverse_bytes.h", - "spinning_mutex.cc", - "spinning_mutex.h", - "tagging.cc", - "tagging.h", - "thread_cache.cc", - "thread_cache.h", - "yield_processor.h", - ] - - if (use_starscan) { - sources += [ - "starscan/logging.h", - "starscan/metadata_allocator.cc", - "starscan/metadata_allocator.h", - "starscan/pcscan.cc", - "starscan/pcscan.h", - "starscan/pcscan_internal.cc", - "starscan/pcscan_internal.h", - "starscan/pcscan_scheduling.cc", - "starscan/pcscan_scheduling.h", - "starscan/raceful_worklist.h", - "starscan/scan_loop.h", - "starscan/snapshot.cc", - "starscan/snapshot.h", - "starscan/stack/stack.cc", - "starscan/stack/stack.h", - "starscan/starscan_fwd.h", - "starscan/state_bitmap.h", - "starscan/stats_collector.cc", - "starscan/stats_collector.h", - "starscan/stats_reporter.h", - "starscan/write_protector.cc", - "starscan/write_protector.h", - ] - } - - defines = [] - if (is_win) { - sources += [ - "page_allocator_internals_win.h", - "partition_alloc_base/rand_util_win.cc", - "partition_alloc_base/scoped_clear_last_error_win.cc", - "partition_alloc_base/threading/platform_thread_win.cc", - "partition_alloc_base/time/time_win.cc", - "partition_tls_win.cc", - ] - } else if (is_posix) { - sources += [ - "page_allocator_internals_posix.cc", - "page_allocator_internals_posix.h", - "partition_alloc_base/files/file_util.h", - "partition_alloc_base/files/file_util_posix.cc", - "partition_alloc_base/posix/safe_strerror.cc", - "partition_alloc_base/posix/safe_strerror.h", - "partition_alloc_base/rand_util_posix.cc", - "partition_alloc_base/threading/platform_thread_internal_posix.h", - "partition_alloc_base/threading/platform_thread_posix.cc", - "partition_alloc_base/time/time_conversion_posix.cc", - ] - - if (is_android || is_chromeos_ash) { - sources += [ "partition_alloc_base/time/time_android.cc" ] - } - if (is_apple) { - sources += [ "partition_alloc_base/time/time_mac.mm" ] - } else { - sources += [ "partition_alloc_base/time/time_now_posix.cc" ] - } - } else if (is_fuchsia) { - sources += [ - "page_allocator_internals_fuchsia.h", - "partition_alloc_base/fuchsia/fuchsia_logging.cc", - "partition_alloc_base/fuchsia/fuchsia_logging.h", - "partition_alloc_base/posix/safe_strerror.cc", - "partition_alloc_base/posix/safe_strerror.h", - "partition_alloc_base/rand_util_fuchsia.cc", - "partition_alloc_base/threading/platform_thread_internal_posix.h", - "partition_alloc_base/threading/platform_thread_posix.cc", - "partition_alloc_base/time/time_conversion_posix.cc", - "partition_alloc_base/time/time_fuchsia.cc", - ] - } - if (is_android) { - # Only android build requires native_library, and native_library depends - # on file_path. So file_path is added if is_android = true. - sources += [ - "partition_alloc_base/files/file_path.cc", - "partition_alloc_base/files/file_path.h", - "partition_alloc_base/native_library.cc", - "partition_alloc_base/native_library.h", - "partition_alloc_base/native_library_posix.cc", - ] - } - if (is_apple) { - # Apple-specific utilities - sources += [ - "partition_alloc_base/mac/foundation_util.h", - "partition_alloc_base/mac/foundation_util.mm", - "partition_alloc_base/mac/scoped_cftyperef.h", - "partition_alloc_base/mac/scoped_typeref.h", - ] - if (is_ios) { - sources += [ - "partition_alloc_base/ios/ios_util.h", - "partition_alloc_base/ios/ios_util.mm", - "partition_alloc_base/system/sys_info_ios.mm", - ] - } - if (is_mac) { - sources += [ - "partition_alloc_base/mac/mac_util.h", - "partition_alloc_base/mac/mac_util.mm", - "partition_alloc_base/system/sys_info_mac.mm", - ] - } - } - if (use_starscan) { - if (current_cpu == "x64") { - assert(pcscan_stack_supported) - sources += [ "starscan/stack/asm/x64/push_registers_asm.cc" ] - } else if (current_cpu == "x86") { - assert(pcscan_stack_supported) - sources += [ "starscan/stack/asm/x86/push_registers_asm.cc" ] - } else if (current_cpu == "arm") { - assert(pcscan_stack_supported) - sources += [ "starscan/stack/asm/arm/push_registers_asm.cc" ] - } else if (current_cpu == "arm64") { - assert(pcscan_stack_supported) - sources += [ "starscan/stack/asm/arm64/push_registers_asm.cc" ] - } else { - # To support a trampoline for another arch, please refer to v8/src/heap/base. - assert(!pcscan_stack_supported) - } - } - public_deps = [ - ":chromecast_buildflags", - ":chromeos_buildflags", - ":debugging_buildflags", - ":logging_buildflags", - ":partition_alloc_buildflags", - ] - - configs += [ - ":partition_alloc_implementation", - ":memory_tagging", - ] - deps = [] - public_configs = [] - if (is_android && !use_cobalt_customizations) { - # tagging.cc requires __arm_mte_set_* functions. - deps += [ "//third_party/android_ndk:cpu_features" ] - } - if (is_fuchsia) { - public_deps += [ - "//third_party/fuchsia-sdk/sdk/pkg/fit", - "//third_party/fuchsia-sdk/sdk/pkg/sync", - "//third_party/fuchsia-sdk/sdk/pkg/zx", - ] - - # Needed for users of spinning_mutex.h, which for performance reasons, - # contains inlined calls to `libsync` inside the header file. - # It appends an entry to the "libs" section of the dependent target. - public_configs += [ ":fuchsia_sync_lib" ] - } - - frameworks = [] - if (is_mac) { - # SecTaskGetCodeSignStatus needs: - frameworks += [ "Security.framework" ] - } - - if (is_apple) { - frameworks += [ - "CoreFoundation.framework", - "Foundation.framework", - ] - } - - configs += [ "//build/config/compiler:wexit_time_destructors" ] - - # Partition alloc is relatively hot (>1% of cycles for users of CrOS). Use speed-focused - # optimizations for it. - if (!is_debug) { - configs -= [ "//build/config/compiler:default_optimization" ] - configs += [ "//build/config/compiler:optimize_speed" ] - } - - # We want to be able to test pkey mode without access to the default pkey. - # This is incompatible with stack protectors since the TLS won't be pkey-tagged. - if (enable_pkeys && is_debug) { - configs += [ ":no_stack_protector" ] - } - - # Need to include |component("partition_alloc")| for gn check. - if (is_starboard) { - # TODO: b/326459868 - Try using |check_includes = false|. - sources = [] - } -} - -source_set("raw_ptr") { - # `gn check` is unhappy with most `#includes` when PA isn't - # actually built. - check_includes = use_partition_alloc - public = [ - "pointers/raw_ptr.h", - "pointers/raw_ptr_exclusion.h", - "pointers/raw_ref.h", - ] - sources = [] - if (enable_backup_ref_ptr_support) { - sources += [ - "pointers/raw_ptr_backup_ref_impl.cc", - "pointers/raw_ptr_backup_ref_impl.h", - ] - } else if (use_hookable_raw_ptr) { - sources += [ - "pointers/raw_ptr_hookable_impl.cc", - "pointers/raw_ptr_hookable_impl.h", - ] - } else if (use_asan_unowned_ptr) { - sources += [ - "pointers/raw_ptr_asan_unowned_impl.cc", - "pointers/raw_ptr_asan_unowned_impl.h", - ] - } - if (use_partition_alloc) { - public_deps = [ ":partition_alloc" ] - } - deps = [ ":buildflags" ] - - # See also: `partition_alloc_base/component_export.h` - defines = [ "IS_RAW_PTR_IMPL" ] - - # When built inside Chromium, although this cannot directly be made a - # component, we expect `//base` to provide the only GN-level access. - if (build_with_chromium) { - visibility = [ "//base" ] - } -} - -buildflag_header("partition_alloc_buildflags") { - header = "partition_alloc_buildflags.h" - - _record_alloc_info = false - - # GWP-ASan is tied to BRP's "refcount in previous slot" mode, whose - # enablement is already gated on BRP enablement. - _enable_gwp_asan_support = put_ref_count_in_previous_slot - - # TODO(crbug.com/1151236): Need to refactor the following buildflags. - # The buildflags (except RECORD_ALLOC_INFO) are used by both chrome and - # partition alloc. For partition alloc, - # gen/base/allocator/partition_allocator/partition_alloc_buildflags.h - # defines and partition alloc includes the header file. For chrome, - # gen/base/allocator/buildflags.h defines and chrome includes. - flags = [ - "HAS_64_BIT_POINTERS=$has_64_bit_pointers", - - "USE_PARTITION_ALLOC=$use_partition_alloc", - "USE_PARTITION_ALLOC_AS_MALLOC=$use_partition_alloc_as_malloc", - - "ENABLE_BACKUP_REF_PTR_SUPPORT=$enable_backup_ref_ptr_support", - "ENABLE_BACKUP_REF_PTR_SLOW_CHECKS=$enable_backup_ref_ptr_slow_checks", - "ENABLE_BACKUP_REF_PTR_FEATURE_FLAG=$enable_backup_ref_ptr_feature_flag", - "ENABLE_RAW_PTR_EXPERIMENTAL=$enable_raw_ptr_experimental", - "ENABLE_DANGLING_RAW_PTR_CHECKS=$enable_dangling_raw_ptr_checks", - "ENABLE_DANGLING_RAW_PTR_FEATURE_FLAG=$enable_dangling_raw_ptr_feature_flag", - "ENABLE_DANGLING_RAW_PTR_PERF_EXPERIMENT=$enable_dangling_raw_ptr_perf_experiment", - "ENABLE_POINTER_SUBTRACTION_CHECK=$enable_pointer_subtraction_check", - "BACKUP_REF_PTR_POISON_OOB_PTR=$backup_ref_ptr_poison_oob_ptr", - "PUT_REF_COUNT_IN_PREVIOUS_SLOT=$put_ref_count_in_previous_slot", - "USE_ASAN_BACKUP_REF_PTR=$use_asan_backup_ref_ptr", - "USE_ASAN_UNOWNED_PTR=$use_asan_unowned_ptr", - "USE_HOOKABLE_RAW_PTR=$use_hookable_raw_ptr", - "ENABLE_GWP_ASAN_SUPPORT=$_enable_gwp_asan_support", - - "FORCE_ENABLE_RAW_PTR_EXCLUSION=$force_enable_raw_ptr_exclusion", - - "RECORD_ALLOC_INFO=$_record_alloc_info", - "USE_FREESLOT_BITMAP=$use_freeslot_bitmap", - "GLUE_CORE_POOLS=$glue_core_pools", - "ENABLE_POINTER_COMPRESSION=$enable_pointer_compression_support", - "ENABLE_SHADOW_METADATA_FOR_64_BITS_POINTERS=$enable_shadow_metadata", - - "USE_STARSCAN=$use_starscan", - "PCSCAN_STACK_SUPPORTED=$pcscan_stack_supported", - - "ENABLE_PKEYS=$enable_pkeys", - ] - - if (is_apple) { - # TODO(crbug.com/1414153): once TimeTicks::Now behavior is unified on iOS, - # this should be removed. - flags += [ "PARTITION_ALLOC_ENABLE_MACH_ABSOLUTE_TIME_TICKS=" + - "$partition_alloc_enable_mach_absolute_time_ticks" ] - } -} - -buildflag_header("chromecast_buildflags") { - header = "chromecast_buildflags.h" - - flags = [ - "PA_IS_CAST_ANDROID=$is_cast_android", - "PA_IS_CASTOS=$is_castos", - ] -} - -buildflag_header("chromeos_buildflags") { - header = "chromeos_buildflags.h" - - flags = [ "PA_IS_CHROMEOS_ASH=$is_chromeos_ash" ] -} - -buildflag_header("logging_buildflags") { - header = "logging_buildflags.h" - - flags = [ "PA_ENABLE_LOG_ERROR_NOT_REACHED=$enable_log_error_not_reached" ] -} - -buildflag_header("debugging_buildflags") { - header = "debugging_buildflags.h" - header_dir = rebase_path(".", "//") + "/partition_alloc_base/debug" - - # Duplicates the setup Chromium uses to define `DCHECK_IS_ON()`, - # but avails it as a buildflag. - _dcheck_is_on = is_debug || dcheck_always_on - - flags = [ - "PA_DCHECK_IS_ON=$_dcheck_is_on", - "PA_EXPENSIVE_DCHECKS_ARE_ON=$enable_expensive_dchecks", - "PA_DCHECK_IS_CONFIGURABLE=$dcheck_is_configurable", - ] -} - -group("buildflags") { - public_deps = [ - ":chromecast_buildflags", - ":chromeos_buildflags", - ":debugging_buildflags", - ":logging_buildflags", - ":partition_alloc_buildflags", - ] -} -# TODO(crbug.com/1151236): After making partition_alloc a standalone library, -# move test code here. i.e. test("partition_alloc_tests") { ... } and -# test("partition_alloc_perftests").
diff --git a/base/allocator/partition_allocator/DEPS b/base/allocator/partition_allocator/DEPS deleted file mode 100644 index 4de2f2c..0000000 --- a/base/allocator/partition_allocator/DEPS +++ /dev/null
@@ -1,169 +0,0 @@ -# Copyright 2021 The Chromium Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -# PartitionAlloc is planned to be extracted into a standalone library, and -# therefore dependencies need to be strictly controlled and minimized. - -gclient_gn_args_file = 'partition_allocator/build/config/gclient_args.gni' - -# Only these hosts are allowed for dependencies in this DEPS file. -# This is a subset of chromium/src/DEPS's allowed_hosts. -allowed_hosts = [ - 'chromium.googlesource.com', -] - -vars = { - 'chromium_git': 'https://chromium.googlesource.com', -} - -deps = { - 'partition_allocator/build': - Var('chromium_git') + '/chromium/src/build.git', - 'partition_allocator/buildtools': - Var('chromium_git') + '/chromium/src/buildtools.git', - 'partition_allocator/buildtools/clang_format/script': - Var('chromium_git') + - '/external/github.com/llvm/llvm-project/clang/tools/clang-format.git', - 'partition_allocator/buildtools/linux64': { - 'packages': [ - { - 'package': 'gn/gn/linux-${{arch}}', - 'version': 'latest', - } - ], - 'dep_type': 'cipd', - 'condition': 'host_os == "linux"', - }, - 'partition_allocator/buildtools/mac': { - 'packages': [ - { - 'package': 'gn/gn/mac-${{arch}}', - 'version': 'latest', - } - ], - 'dep_type': 'cipd', - 'condition': 'host_os == "mac"', - }, - 'partition_allocator/buildtools/win': { - 'packages': [ - { - 'package': 'gn/gn/windows-amd64', - 'version': 'latest', - } - ], - 'dep_type': 'cipd', - 'condition': 'host_os == "win"', - }, - 'partition_allocator/buildtools/third_party/libc++/trunk': - Var('chromium_git') + '/external/github.com/llvm/llvm-project/libcxx.git', - 'partition_allocator/buildtools/third_party/libc++abi/trunk': - Var('chromium_git') + - '/external/github.com/llvm/llvm-project/libcxxabi.git', - 'partition_allocator/tools/clang': - Var('chromium_git') + '/chromium/src/tools/clang.git', -} - -hooks = [ - { - 'name': 'sysroot_arm', - 'pattern': '.', - 'condition': 'checkout_linux and checkout_arm', - 'action': [ - 'python3', - 'partition_allocator/build/linux/sysroot_scripts/install-sysroot.py', - '--arch=arm'], - }, - { - 'name': 'sysroot_arm64', - 'pattern': '.', - 'condition': 'checkout_linux and checkout_arm64', - 'action': [ - 'python3', - 'partition_allocator/build/linux/sysroot_scripts/install-sysroot.py', - '--arch=arm64'], - }, - { - 'name': 'sysroot_x86', - 'pattern': '.', - 'condition': 'checkout_linux and (checkout_x86 or checkout_x64)', - 'action': [ - 'python3', - 'partition_allocator/build/linux/sysroot_scripts/install-sysroot.py', - '--arch=x86'], - }, - { - 'name': 'sysroot_mips', - 'pattern': '.', - 'condition': 'checkout_linux and checkout_mips', - 'action': [ - 'python3', - 'partition_allocator/build/linux/sysroot_scripts/install-sysroot.py', - '--arch=mips'], - }, - { - 'name': 'sysroot_mips64', - 'pattern': '.', - 'condition': 'checkout_linux and checkout_mips64', - 'action': [ - 'python3', - 'partition_allocator/build/linux/sysroot_scripts/install-sysroot.py', - '--arch=mips64el'], - }, - { - 'name': 'sysroot_x64', - 'pattern': '.', - 'condition': 'checkout_linux and checkout_x64', - 'action': [ - 'python3', - 'partition_allocator/build/linux/sysroot_scripts/install-sysroot.py', - '--arch=x64'], - }, - { - # Update the prebuilt clang toolchain. - # Note: On Win, this should run after win_toolchain, as it may use it. - 'name': 'clang', - 'pattern': '.', - 'action': ['python3', 'partition_allocator/tools/clang/scripts/update.py'], - }, -] - -noparent = True - -include_rules = [ - "+build/build_config.h", - "+build/buildflag.h", - "+third_party/lss/linux_syscall_support.h", -] - -specific_include_rules = { - ".*_(perf|unit)test\.cc$": [ - "+base/allocator/allocator_shim_default_dispatch_to_partition_alloc.h", - "+base/allocator/dispatcher/dispatcher.h", - "+base/debug/allocation_trace.h", - "+base/debug/debugging_buildflags.h", - "+base/debug/proc_maps_linux.h", - "+base/system/sys_info.h", - "+base/test/gtest_util.h", - "+base/timer/lap_timer.h", - "+base/win/windows_version.h", - "+testing/gmock/include/gmock/gmock.h", - "+testing/gtest/include/gtest/gtest.h", - "+testing/perf/perf_result_reporter.h", - ], - "extended_api\.cc$": [ - "+base/allocator/allocator_shim_default_dispatch_to_partition_alloc.h", - ], - "gtest_prod_util\.h$": [ - "+testing/gtest/include/gtest/gtest_prod.h", - ], - "raw_(ptr|ref)_unittest\.cc$": [ - "+base", - "+third_party/abseil-cpp/absl/types/optional.h", - "+third_party/abseil-cpp/absl/types/variant.h", - ], - "raw_ptr_test_support\.h$": [ - "+testing/gmock/include/gmock/gmock.h", - "+third_party/abseil-cpp/absl/types/optional.h", - ] -}
diff --git a/base/allocator/partition_allocator/DIR_METADATA b/base/allocator/partition_allocator/DIR_METADATA deleted file mode 100644 index 41685ca..0000000 --- a/base/allocator/partition_allocator/DIR_METADATA +++ /dev/null
@@ -1,6 +0,0 @@ -monorail { - component: "Blink>MemoryAllocator>Partition" -} - -# Also security-dev@chromium.org -team_email: "platform-architecture-dev@chromium.org"
diff --git a/base/allocator/partition_allocator/OWNERS b/base/allocator/partition_allocator/OWNERS deleted file mode 100644 index 7ce390d..0000000 --- a/base/allocator/partition_allocator/OWNERS +++ /dev/null
@@ -1,8 +0,0 @@ -bartekn@chromium.org -haraken@chromium.org -keishi@chromium.org -lizeb@chromium.org -tasak@google.com - -per-file pointers/raw_ptr*=file://base/memory/MIRACLE_PTR_OWNERS -per-file pointers/raw_ref*=file://base/memory/MIRACLE_PTR_OWNERS
diff --git a/base/allocator/partition_allocator/PartitionAlloc.md b/base/allocator/partition_allocator/PartitionAlloc.md deleted file mode 100644 index 4cc3a48..0000000 --- a/base/allocator/partition_allocator/PartitionAlloc.md +++ /dev/null
@@ -1,203 +0,0 @@ -# PartitionAlloc Design - -This document describes PartitionAlloc at a high level, with some architectural -details. For implementation details, see the comments in -`partition_alloc_constants.h`. - -## Quick Links - -* [Glossary](./glossary.md): Definitions of terms commonly used in - PartitionAlloc. The present document largely avoids defining terms. - -* [Build Config](./build_config.md): Pertinent GN args, buildflags, and - macros. - -* [Chrome-External Builds](./external_builds.md): Further considerations - for standalone PartitionAlloc, plus an embedder's guide for some extra - GN args. - -## Overview - -PartitionAlloc is a memory allocator optimized for space efficiency, -allocation latency, and security. - -### Performance - -PartitionAlloc is designed to be extremely fast in its fast paths. The fast -paths of allocation and deallocation require very few (reasonably predictable) -branches. The number of operations in the fast paths is minimal, leading to the -possibility of inlining. - - - -However, even the fast path isn't the fastest, because it requires taking -a per-partition lock. Although we optimized the lock, there was still room for -improvement; to this end, we introduced the thread cache. -The thread cache has been tailored to satisfy a vast majority of requests by -allocating from and releasing memory to the main allocator in batches, -amortizing lock acquisition and further improving locality while not trapping -excess memory. - -### Security - -Security is one of the important goals of PartitionAlloc. - -PartitionAlloc guarantees that different partitions exist in different regions -of the process's address space. When the caller has freed all objects contained -in a page in a partition, PartitionAlloc returns the physical memory to the -operating system, but continues to reserve the region of address space. -PartitionAlloc will only reuse an address space region for the same partition. - -Similarly, one page can contain only objects from the same bucket. -When freed, PartitionAlloc returns the physical memory, but continues to reserve -the region for this very bucket. - -The above techniques help avoid type confusion attacks. Note, however, these -apply only to normal buckets and not to direct map, as it'd waste too much -address space. - -PartitionAlloc also guarantees that: - -* Linear overflows/underflows cannot corrupt into, out of, or between - partitions. There are guard pages at the beginning and the end of each memory - region owned by a partition. - -* Linear overflows/underflows cannot corrupt the allocation metadata. - PartitionAlloc records metadata in a dedicated, out-of-line region (not - adjacent to objects), surrounded by guard pages. (Freelist pointers are an - exception.) - -* Partial pointer overwrite of freelist pointer should fault. - -* Direct map allocations have guard pages at the beginning and the end. - -### Alignment - -PartitionAlloc guarantees that returned pointers are aligned on -`partition_alloc::internal::kAlignment` boundary (typically 16B on -64-bit systems, and 8B on 32-bit). - -PartitionAlloc also supports higher levels of alignment, that can be requested -via `PartitionAlloc::AlignedAllocWithFlags()` or platform-specific APIs (such as -`posix_memalign()`). The requested -alignment has to be a power of two. PartitionAlloc reserves the right to round -up the requested size to the nearest power of two, greater than or equal to the -requested alignment. This may be wasteful, but allows taking advantage of -natural PartitionAlloc alignment guarantees. Allocations with an alignment -requirement greater than `partition_alloc::internal::kAlignment` are expected -to be very rare. - -## Architecture - -### Layout in Memory - -PartitionAlloc handles normal buckets by reserving (not committing) 2MiB super -pages. Each super page is split into partition pages. -The first and the last partition page are permanently inaccessible and serve -as guard pages, with the exception of one system page in the middle of the first -partition page that holds metadata (32B struct per partition page). - - - -* The slot span numbers provide a visual hint of their size (in partition - pages). -* Colors provide a visual hint of the bucket to which the slot span belongs. - * Although only five colors are shown, in reality, a super page holds - tens of slot spans, some of which belong to the same bucket. -* The system page that holds metadata tracks each partition page with one 32B - [`PartitionPage` struct][PartitionPage], which is either - * a [`SlotSpanMetadata`][SlotSpanMetadata] ("v"s in the diagram) or - * a [`SubsequentPageMetadata`][SubsequentPageMetadata] ("+"s in the - diagram). -* Gray fill denotes guard pages (one partition page each at the head and tail - of each super page). -* In some configurations, PartitionAlloc stores more metadata than can - fit in the one system page at the front. These are the bitmaps for - StarScan and `MTECheckedPtr<T>`, and they are relegated to the head of - what would otherwise be usable space for slot spans. One, both, or - none of these bitmaps may be present, depending on build - configuration, runtime configuration, and type of allocation. - See [`SuperPagePayloadBegin()`][payload-start] for details. - -As allocation requests arrive, there is eventually a need to allocate a new slot -span. -Address space for such a slot span is carved out from the last super page. If -not enough space, a new super page is allocated. Due to varying sizes of slot -span, this may lead to leaving space unused (we never go back to fill previous -super pages), which is fine because this memory is merely reserved, which is far -less precious than committed memory. Note also that address space reserved for a -slot span is never released, even if the slot span isn't used for a long time. - -All slots in a newly allocated slot span are *free*, i.e. available for -allocation. - -### Freelist Pointers - -All free slots within a slot span are chained into a singly-linked free-list, -by writing the *next* pointer at the beginning of each slot, and the head of the -list is written in the metadata struct. - -However, writing a pointer in each free slot of a newly allocated span would -require committing and faulting in physical pages upfront, which would be -unacceptable. Therefore, PartitionAlloc has a concept of *provisioning slots*. -Only provisioned slots are chained into the freelist. -Once provisioned slots in a span are depleted, then another page worth of slots -is provisioned (note, a slot that crosses a page boundary only gets -provisioned with slots of the next page). See -`PartitionBucket::ProvisionMoreSlotsAndAllocOne()` for more details. - -Freelist pointers are stored at the beginning of each free slot. As such, they -are the only metadata that is inline, i.e. stored among the -objects. This makes them prone to overruns. On little-endian systems, the -pointers are encoded by reversing byte order, so that partial overruns will very -likely result in destroying the pointer, as opposed to forming a valid pointer -to a nearby location. - -Furthermore, a shadow of a freelist pointer is stored next to it, encoded in a -different manner. This helps PartitionAlloc detect corruptions. - -### Slot Span States - -A slot span can be in any of 4 states: -* *Full*. A full span has no free slots. -* *Empty*. An empty span has no allocated slots, only free slots. -* *Active*. An active span is anything in between the above two. -* *Decommitted*. A decommitted span is a special case of an empty span, where - all pages are decommitted from memory. - -PartitionAlloc prioritizes getting an available slot from an active span, over -an empty one, in hope that the latter can be soon transitioned into a -decommitted state, thus releasing memory. There is no mechanism, however, to -prioritize selection of a slot span based on the number of already allocated -slots. - -An empty span becomes decommitted either when there are too many empty spans -(FIFO), or when `PartitionRoot::PurgeMemory()` gets invoked periodically (or in -low memory pressure conditions). An allocation can be satisfied from -a decommitted span if there are no active or empty spans available. The slot -provisioning mechanism kicks back in, committing the pages gradually as needed, -and the span becomes active. (There is currently no other way -to unprovision slots than decommitting the entire span). - -As mentioned above, a bucket is a collection of slot spans containing slots of -the same size. In fact, each bucket has 3 linked-lists, chaining active, empty -and decommitted spans (see `PartitionBucket::*_slot_spans_head`). -There is no need for a full span list. The lists are updated lazily. An empty, -decommitted or full span may stay on the active list for some time, until -`PartitionBucket::SetNewActiveSlotSpan()` encounters it. -A decommitted span may stay on the empty list for some time, -until `PartitionBucket<thread_safe>::SlowPathAlloc()` encounters it. However, -the inaccuracy can't happen in the other direction, i.e. an active span can only -be on the active list, and an empty span can only be on the active or empty -list. - -[PartitionPage]: https://source.chromium.org/chromium/chromium/src/+/main:base/allocator/partition_allocator/partition_page.h;l=314;drc=e5b03e85ea180d1d1ab0dec471c7fd5d1706a9e4 -[SlotSpanMetadata]: https://source.chromium.org/chromium/chromium/src/+/main:base/allocator/partition_allocator/partition_page.h;l=120;drc=e5b03e85ea180d1d1ab0dec471c7fd5d1706a9e4 -[SubsequentPageMetadata]: https://source.chromium.org/chromium/chromium/src/+/main:base/allocator/partition_allocator/partition_page.h;l=295;drc=e5b03e85ea180d1d1ab0dec471c7fd5d1706a9e4 -[payload-start]: https://source.chromium.org/chromium/chromium/src/+/35b2deed603dedd4abb37f204d516ed62aa2b85c:base/allocator/partition_allocator/partition_page.h;l=454
diff --git a/base/allocator/partition_allocator/address_pool_manager.cc b/base/allocator/partition_allocator/address_pool_manager.cc deleted file mode 100644 index d00158d..0000000 --- a/base/allocator/partition_allocator/address_pool_manager.cc +++ /dev/null
@@ -1,553 +0,0 @@ -// 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. - -#include "base/allocator/partition_allocator/address_pool_manager.h" - -#include <algorithm> -#include <atomic> -#include <cstdint> -#include <limits> - -#include "base/allocator/partition_allocator/address_space_stats.h" -#include "base/allocator/partition_allocator/page_allocator.h" -#include "base/allocator/partition_allocator/page_allocator_constants.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "base/allocator/partition_allocator/partition_alloc_notreached.h" -#include "base/allocator/partition_allocator/pkey.h" -#include "base/allocator/partition_allocator/reservation_offset_table.h" -#include "build/build_config.h" - -#if BUILDFLAG(IS_APPLE) || BUILDFLAG(ENABLE_PKEYS) -#include <sys/mman.h> -#endif - -namespace partition_alloc::internal { - -AddressPoolManager AddressPoolManager::singleton_; - -// static -AddressPoolManager& AddressPoolManager::GetInstance() { - return singleton_; -} - -#if BUILDFLAG(HAS_64_BIT_POINTERS) - -namespace { - -// This will crash if the range cannot be decommitted. -void DecommitPages(uintptr_t address, size_t size) { - // Callers rely on the pages being zero-initialized when recommitting them. - // |DecommitSystemPages| doesn't guarantee this on all operating systems, in - // particular on macOS, but |DecommitAndZeroSystemPages| does. - DecommitAndZeroSystemPages(address, size); -} - -} // namespace - -void AddressPoolManager::Add(pool_handle handle, uintptr_t ptr, size_t length) { - PA_DCHECK(!(ptr & kSuperPageOffsetMask)); - PA_DCHECK(!((ptr + length) & kSuperPageOffsetMask)); - PA_CHECK(handle > 0 && handle <= std::size(aligned_pools_.pools_)); - - Pool* pool = GetPool(handle); - PA_CHECK(!pool->IsInitialized()); - pool->Initialize(ptr, length); -} - -void AddressPoolManager::GetPoolUsedSuperPages( - pool_handle handle, - std::bitset<kMaxSuperPagesInPool>& used) { - Pool* pool = GetPool(handle); - if (!pool) { - return; - } - - pool->GetUsedSuperPages(used); -} - -uintptr_t AddressPoolManager::GetPoolBaseAddress(pool_handle handle) { - Pool* pool = GetPool(handle); - if (!pool) { - return 0; - } - - return pool->GetBaseAddress(); -} - -void AddressPoolManager::ResetForTesting() { - for (size_t i = 0; i < std::size(aligned_pools_.pools_); ++i) { - aligned_pools_.pools_[i].Reset(); - } -} - -void AddressPoolManager::Remove(pool_handle handle) { - Pool* pool = GetPool(handle); - PA_DCHECK(pool->IsInitialized()); - pool->Reset(); -} - -uintptr_t AddressPoolManager::Reserve(pool_handle handle, - uintptr_t requested_address, - size_t length) { - Pool* pool = GetPool(handle); - if (!requested_address) { - return pool->FindChunk(length); - } - const bool is_available = pool->TryReserveChunk(requested_address, length); - if (is_available) { - return requested_address; - } - return pool->FindChunk(length); -} - -void AddressPoolManager::UnreserveAndDecommit(pool_handle handle, - uintptr_t address, - size_t length) { - PA_DCHECK(kNullPoolHandle < handle && handle <= kNumPools); - Pool* pool = GetPool(handle); - PA_DCHECK(pool->IsInitialized()); - DecommitPages(address, length); - pool->FreeChunk(address, length); -} - -void AddressPoolManager::Pool::Initialize(uintptr_t ptr, size_t length) { - PA_CHECK(ptr != 0); - PA_CHECK(!(ptr & kSuperPageOffsetMask)); - PA_CHECK(!(length & kSuperPageOffsetMask)); - address_begin_ = ptr; -#if BUILDFLAG(PA_DCHECK_IS_ON) - address_end_ = ptr + length; - PA_DCHECK(address_begin_ < address_end_); -#endif - - total_bits_ = length / kSuperPageSize; - PA_CHECK(total_bits_ <= kMaxSuperPagesInPool); - - ScopedGuard scoped_lock(lock_); - alloc_bitset_.reset(); - bit_hint_ = 0; -} - -bool AddressPoolManager::Pool::IsInitialized() { - return address_begin_ != 0; -} - -void AddressPoolManager::Pool::Reset() { - address_begin_ = 0; -} - -void AddressPoolManager::Pool::GetUsedSuperPages( - std::bitset<kMaxSuperPagesInPool>& used) { - ScopedGuard scoped_lock(lock_); - - PA_DCHECK(IsInitialized()); - used = alloc_bitset_; -} - -uintptr_t AddressPoolManager::Pool::GetBaseAddress() { - PA_DCHECK(IsInitialized()); - return address_begin_; -} - -uintptr_t AddressPoolManager::Pool::FindChunk(size_t requested_size) { - ScopedGuard scoped_lock(lock_); - - PA_DCHECK(!(requested_size & kSuperPageOffsetMask)); - const size_t need_bits = requested_size >> kSuperPageShift; - - // Use first-fit policy to find an available chunk from free chunks. Start - // from |bit_hint_|, because we know there are no free chunks before. - size_t beg_bit = bit_hint_; - size_t curr_bit = bit_hint_; - while (true) { - // |end_bit| points 1 past the last bit that needs to be 0. If it goes past - // |total_bits_|, return |nullptr| to signal no free chunk was found. - size_t end_bit = beg_bit + need_bits; - if (end_bit > total_bits_) { - return 0; - } - - bool found = true; - for (; curr_bit < end_bit; ++curr_bit) { - if (alloc_bitset_.test(curr_bit)) { - // The bit was set, so this chunk isn't entirely free. Set |found=false| - // to ensure the outer loop continues. However, continue the inner loop - // to set |beg_bit| just past the last set bit in the investigated - // chunk. |curr_bit| is advanced all the way to |end_bit| to prevent the - // next outer loop pass from checking the same bits. - beg_bit = curr_bit + 1; - found = false; - if (bit_hint_ == curr_bit) { - ++bit_hint_; - } - } - } - - // An entire [beg_bit;end_bit) region of 0s was found. Fill them with 1s (to - // mark as allocated) and return the allocated address. - if (found) { - for (size_t i = beg_bit; i < end_bit; ++i) { - PA_DCHECK(!alloc_bitset_.test(i)); - alloc_bitset_.set(i); - } - if (bit_hint_ == beg_bit) { - bit_hint_ = end_bit; - } - uintptr_t address = address_begin_ + beg_bit * kSuperPageSize; -#if BUILDFLAG(PA_DCHECK_IS_ON) - PA_DCHECK(address + requested_size <= address_end_); -#endif - return address; - } - } - - PA_NOTREACHED(); - return 0; -} - -bool AddressPoolManager::Pool::TryReserveChunk(uintptr_t address, - size_t requested_size) { - ScopedGuard scoped_lock(lock_); - PA_DCHECK(!(address & kSuperPageOffsetMask)); - PA_DCHECK(!(requested_size & kSuperPageOffsetMask)); - const size_t begin_bit = (address - address_begin_) / kSuperPageSize; - const size_t need_bits = requested_size / kSuperPageSize; - const size_t end_bit = begin_bit + need_bits; - // Check that requested address is not too high. - if (end_bit > total_bits_) { - return false; - } - // Check if any bit of the requested region is set already. - for (size_t i = begin_bit; i < end_bit; ++i) { - if (alloc_bitset_.test(i)) { - return false; - } - } - // Otherwise, set the bits. - for (size_t i = begin_bit; i < end_bit; ++i) { - alloc_bitset_.set(i); - } - return true; -} - -void AddressPoolManager::Pool::FreeChunk(uintptr_t address, size_t free_size) { - ScopedGuard scoped_lock(lock_); - - PA_DCHECK(!(address & kSuperPageOffsetMask)); - PA_DCHECK(!(free_size & kSuperPageOffsetMask)); - - PA_DCHECK(address_begin_ <= address); -#if BUILDFLAG(PA_DCHECK_IS_ON) - PA_DCHECK(address + free_size <= address_end_); -#endif - - const size_t beg_bit = (address - address_begin_) / kSuperPageSize; - const size_t end_bit = beg_bit + free_size / kSuperPageSize; - for (size_t i = beg_bit; i < end_bit; ++i) { - PA_DCHECK(alloc_bitset_.test(i)); - alloc_bitset_.reset(i); - } - bit_hint_ = std::min(bit_hint_, beg_bit); -} - -void AddressPoolManager::Pool::GetStats(PoolStats* stats) { - std::bitset<kMaxSuperPagesInPool> pages; - size_t i; - { - ScopedGuard scoped_lock(lock_); - pages = alloc_bitset_; - i = bit_hint_; - } - - stats->usage = pages.count(); - - size_t largest_run = 0; - size_t current_run = 0; - for (; i < total_bits_; ++i) { - if (!pages[i]) { - current_run += 1; - continue; - } else if (current_run > largest_run) { - largest_run = current_run; - } - current_run = 0; - } - - // Fell out of the loop with last bit being zero. Check once more. - if (current_run > largest_run) { - largest_run = current_run; - } - stats->largest_available_reservation = largest_run; -} - -void AddressPoolManager::GetPoolStats(const pool_handle handle, - PoolStats* stats) { - Pool* pool = GetPool(handle); - if (!pool->IsInitialized()) { - return; - } - pool->GetStats(stats); -} - -bool AddressPoolManager::GetStats(AddressSpaceStats* stats) { - // Get 64-bit pool stats. - GetPoolStats(kRegularPoolHandle, &stats->regular_pool_stats); -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - GetPoolStats(kBRPPoolHandle, &stats->brp_pool_stats); -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - if (IsConfigurablePoolAvailable()) { - GetPoolStats(kConfigurablePoolHandle, &stats->configurable_pool_stats); - } -#if BUILDFLAG(ENABLE_PKEYS) - GetPoolStats(kPkeyPoolHandle, &stats->pkey_pool_stats); -#endif - return true; -} - -#else // BUILDFLAG(HAS_64_BIT_POINTERS) - -static_assert( - kSuperPageSize % AddressPoolManagerBitmap::kBytesPer1BitOfBRPPoolBitmap == - 0, - "kSuperPageSize must be a multiple of kBytesPer1BitOfBRPPoolBitmap."); -static_assert( - kSuperPageSize / AddressPoolManagerBitmap::kBytesPer1BitOfBRPPoolBitmap > 0, - "kSuperPageSize must be larger than kBytesPer1BitOfBRPPoolBitmap."); -static_assert(AddressPoolManagerBitmap::kGuardBitsOfBRPPoolBitmap >= - AddressPoolManagerBitmap::kGuardOffsetOfBRPPoolBitmap, - "kGuardBitsOfBRPPoolBitmap must be larger than or equal to " - "kGuardOffsetOfBRPPoolBitmap."); - -template <size_t bitsize> -void SetBitmap(std::bitset<bitsize>& bitmap, - size_t start_bit, - size_t bit_length) { - const size_t end_bit = start_bit + bit_length; - PA_DCHECK(start_bit <= bitsize); - PA_DCHECK(end_bit <= bitsize); - - for (size_t i = start_bit; i < end_bit; ++i) { - PA_DCHECK(!bitmap.test(i)); - bitmap.set(i); - } -} - -template <size_t bitsize> -void ResetBitmap(std::bitset<bitsize>& bitmap, - size_t start_bit, - size_t bit_length) { - const size_t end_bit = start_bit + bit_length; - PA_DCHECK(start_bit <= bitsize); - PA_DCHECK(end_bit <= bitsize); - - for (size_t i = start_bit; i < end_bit; ++i) { - PA_DCHECK(bitmap.test(i)); - bitmap.reset(i); - } -} - -uintptr_t AddressPoolManager::Reserve(pool_handle handle, - uintptr_t requested_address, - size_t length) { - PA_DCHECK(!(length & DirectMapAllocationGranularityOffsetMask())); - uintptr_t address = - AllocPages(requested_address, length, kSuperPageSize, - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kInaccessible), - PageTag::kPartitionAlloc); - return address; -} - -void AddressPoolManager::UnreserveAndDecommit(pool_handle handle, - uintptr_t address, - size_t length) { - PA_DCHECK(!(address & kSuperPageOffsetMask)); - PA_DCHECK(!(length & DirectMapAllocationGranularityOffsetMask())); - FreePages(address, length); -} - -void AddressPoolManager::MarkUsed(pool_handle handle, - uintptr_t address, - size_t length) { - ScopedGuard scoped_lock(AddressPoolManagerBitmap::GetLock()); - // When ENABLE_BACKUP_REF_PTR_SUPPORT is off, BRP pool isn't used. -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - if (handle == kBRPPoolHandle) { - PA_DCHECK( - (length % AddressPoolManagerBitmap::kBytesPer1BitOfBRPPoolBitmap) == 0); - - // Make IsManagedByBRPPoolPool() return false when an address inside the - // first or the last PartitionPageSize()-bytes block is given: - // - // ------+---+---------------+---+---- - // memory ..... | B | managed by PA | B | ... - // regions ------+---+---------------+---+---- - // - // B: PartitionPageSize()-bytes block. This is used internally by the - // allocator and is not available for callers. - // - // This is required to avoid crash caused by the following code: - // { - // // Assume this allocation happens outside of PartitionAlloc. - // raw_ptr<T> ptr = new T[20]; - // for (size_t i = 0; i < 20; i ++) { ptr++; } - // // |ptr| may point to an address inside 'B'. - // } - // - // Suppose that |ptr| points to an address inside B after the loop. If - // IsManagedByBRPPoolPool(ptr) were to return true, ~raw_ptr<T>() would - // crash, since the memory is not allocated by PartitionAlloc. - SetBitmap(AddressPoolManagerBitmap::brp_pool_bits_, - (address >> AddressPoolManagerBitmap::kBitShiftOfBRPPoolBitmap) + - AddressPoolManagerBitmap::kGuardOffsetOfBRPPoolBitmap, - (length >> AddressPoolManagerBitmap::kBitShiftOfBRPPoolBitmap) - - AddressPoolManagerBitmap::kGuardBitsOfBRPPoolBitmap); - } else -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - { - PA_DCHECK(handle == kRegularPoolHandle); - PA_DCHECK( - (length % AddressPoolManagerBitmap::kBytesPer1BitOfRegularPoolBitmap) == - 0); - SetBitmap(AddressPoolManagerBitmap::regular_pool_bits_, - address >> AddressPoolManagerBitmap::kBitShiftOfRegularPoolBitmap, - length >> AddressPoolManagerBitmap::kBitShiftOfRegularPoolBitmap); - } -} - -void AddressPoolManager::MarkUnused(pool_handle handle, - uintptr_t address, - size_t length) { - // Address regions allocated for normal buckets are never released, so this - // function can only be called for direct map. However, do not DCHECK on - // IsManagedByDirectMap(address), because many tests test this function using - // small allocations. - - ScopedGuard scoped_lock(AddressPoolManagerBitmap::GetLock()); - // When ENABLE_BACKUP_REF_PTR_SUPPORT is off, BRP pool isn't used. -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - if (handle == kBRPPoolHandle) { - PA_DCHECK( - (length % AddressPoolManagerBitmap::kBytesPer1BitOfBRPPoolBitmap) == 0); - - // Make IsManagedByBRPPoolPool() return false when an address inside the - // first or the last PartitionPageSize()-bytes block is given. - // (See MarkUsed comment) - ResetBitmap( - AddressPoolManagerBitmap::brp_pool_bits_, - (address >> AddressPoolManagerBitmap::kBitShiftOfBRPPoolBitmap) + - AddressPoolManagerBitmap::kGuardOffsetOfBRPPoolBitmap, - (length >> AddressPoolManagerBitmap::kBitShiftOfBRPPoolBitmap) - - AddressPoolManagerBitmap::kGuardBitsOfBRPPoolBitmap); - } else -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - { - PA_DCHECK(handle == kRegularPoolHandle); - PA_DCHECK( - (length % AddressPoolManagerBitmap::kBytesPer1BitOfRegularPoolBitmap) == - 0); - ResetBitmap( - AddressPoolManagerBitmap::regular_pool_bits_, - address >> AddressPoolManagerBitmap::kBitShiftOfRegularPoolBitmap, - length >> AddressPoolManagerBitmap::kBitShiftOfRegularPoolBitmap); - } -} - -void AddressPoolManager::ResetForTesting() { - ScopedGuard guard(AddressPoolManagerBitmap::GetLock()); - AddressPoolManagerBitmap::regular_pool_bits_.reset(); - AddressPoolManagerBitmap::brp_pool_bits_.reset(); -} - -namespace { - -// Counts super pages in use represented by `bitmap`. -template <size_t bitsize> -size_t CountUsedSuperPages(const std::bitset<bitsize>& bitmap, - const size_t bits_per_super_page) { - size_t count = 0; - size_t bit_index = 0; - - // Stride over super pages. - for (size_t super_page_index = 0; bit_index < bitsize; ++super_page_index) { - // Stride over the bits comprising the super page. - for (bit_index = super_page_index * bits_per_super_page; - bit_index < (super_page_index + 1) * bits_per_super_page && - bit_index < bitsize; - ++bit_index) { - if (bitmap[bit_index]) { - count += 1; - // Move on to the next super page. - break; - } - } - } - return count; -} - -} // namespace - -bool AddressPoolManager::GetStats(AddressSpaceStats* stats) { - std::bitset<AddressPoolManagerBitmap::kRegularPoolBits> regular_pool_bits; - std::bitset<AddressPoolManagerBitmap::kBRPPoolBits> brp_pool_bits; - { - ScopedGuard scoped_lock(AddressPoolManagerBitmap::GetLock()); - regular_pool_bits = AddressPoolManagerBitmap::regular_pool_bits_; - brp_pool_bits = AddressPoolManagerBitmap::brp_pool_bits_; - } // scoped_lock - - // Pool usage is read out from the address pool bitmaps. - // The output stats are sized in super pages, so we interpret - // the bitmaps into super page usage. - static_assert( - kSuperPageSize % - AddressPoolManagerBitmap::kBytesPer1BitOfRegularPoolBitmap == - 0, - "information loss when calculating metrics"); - constexpr size_t kRegularPoolBitsPerSuperPage = - kSuperPageSize / - AddressPoolManagerBitmap::kBytesPer1BitOfRegularPoolBitmap; - - // Get 32-bit pool usage. - stats->regular_pool_stats.usage = - CountUsedSuperPages(regular_pool_bits, kRegularPoolBitsPerSuperPage); -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - static_assert( - kSuperPageSize % AddressPoolManagerBitmap::kBytesPer1BitOfBRPPoolBitmap == - 0, - "information loss when calculating metrics"); - constexpr size_t kBRPPoolBitsPerSuperPage = - kSuperPageSize / AddressPoolManagerBitmap::kBytesPer1BitOfBRPPoolBitmap; - stats->brp_pool_stats.usage = - CountUsedSuperPages(brp_pool_bits, kBRPPoolBitsPerSuperPage); - - // Get blocklist size. - for (const auto& blocked : - AddressPoolManagerBitmap::brp_forbidden_super_page_map_) { - if (blocked.load(std::memory_order_relaxed)) { - stats->blocklist_size += 1; - } - } - - // Count failures in finding non-blocklisted addresses. - stats->blocklist_hit_count = - AddressPoolManagerBitmap::blocklist_hit_count_.load( - std::memory_order_relaxed); -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - return true; -} - -#endif // BUILDFLAG(HAS_64_BIT_POINTERS) - -void AddressPoolManager::DumpStats(AddressSpaceStatsDumper* dumper) { - AddressSpaceStats stats{}; - if (GetStats(&stats)) { - dumper->DumpStats(&stats); - } -} - -} // namespace partition_alloc::internal
diff --git a/base/allocator/partition_allocator/address_pool_manager.h b/base/allocator/partition_allocator/address_pool_manager.h deleted file mode 100644 index a2366cb..0000000 --- a/base/allocator/partition_allocator/address_pool_manager.h +++ /dev/null
@@ -1,181 +0,0 @@ -// 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 BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_POOL_MANAGER_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_POOL_MANAGER_H_ - -#include <bitset> -#include <limits> - -#include "base/allocator/partition_allocator/address_pool_manager_types.h" -#include "base/allocator/partition_allocator/partition_address_space.h" -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_base/thread_annotations.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "base/allocator/partition_allocator/partition_lock.h" -#include "build/build_config.h" - -#if !BUILDFLAG(HAS_64_BIT_POINTERS) -#include "base/allocator/partition_allocator/address_pool_manager_bitmap.h" -#endif - -namespace partition_alloc { - -class AddressSpaceStatsDumper; -struct AddressSpaceStats; -struct PoolStats; - -} // namespace partition_alloc - -namespace partition_alloc::internal { - -// (64bit version) -// AddressPoolManager takes a reserved virtual address space and manages address -// space allocation. -// -// AddressPoolManager (currently) supports up to 4 pools. Each pool manages a -// contiguous reserved address space. Alloc() takes a pool_handle and returns -// address regions from the specified pool. Free() also takes a pool_handle and -// returns the address region back to the manager. -// -// (32bit version) -// AddressPoolManager wraps AllocPages and FreePages and remembers allocated -// address regions using bitmaps. IsManagedByPartitionAlloc*Pool use the bitmaps -// to judge whether a given address is in a pool that supports BackupRefPtr or -// in a pool that doesn't. All PartitionAlloc allocations must be in either of -// the pools. -class PA_COMPONENT_EXPORT(PARTITION_ALLOC) AddressPoolManager { - public: - static AddressPoolManager& GetInstance(); - - AddressPoolManager(const AddressPoolManager&) = delete; - AddressPoolManager& operator=(const AddressPoolManager&) = delete; - -#if BUILDFLAG(HAS_64_BIT_POINTERS) - void Add(pool_handle handle, uintptr_t address, size_t length); - void Remove(pool_handle handle); - - // Populate a |used| bitset of superpages currently in use. - void GetPoolUsedSuperPages(pool_handle handle, - std::bitset<kMaxSuperPagesInPool>& used); - - // Return the base address of a pool. - uintptr_t GetPoolBaseAddress(pool_handle handle); -#endif // BUILDFLAG(HAS_64_BIT_POINTERS) - - // Reserves address space from the pool. - uintptr_t Reserve(pool_handle handle, - uintptr_t requested_address, - size_t length); - - // Frees address space back to the pool and decommits underlying system pages. - void UnreserveAndDecommit(pool_handle handle, - uintptr_t address, - size_t length); - void ResetForTesting(); - -#if !BUILDFLAG(HAS_64_BIT_POINTERS) - void MarkUsed(pool_handle handle, uintptr_t address, size_t size); - void MarkUnused(pool_handle handle, uintptr_t address, size_t size); - - static bool IsManagedByRegularPool(uintptr_t address) { - return AddressPoolManagerBitmap::IsManagedByRegularPool(address); - } - - static bool IsManagedByBRPPool(uintptr_t address) { - return AddressPoolManagerBitmap::IsManagedByBRPPool(address); - } -#endif // !BUILDFLAG(HAS_64_BIT_POINTERS) - - void DumpStats(AddressSpaceStatsDumper* dumper); - - private: - friend class AddressPoolManagerForTesting; -#if BUILDFLAG(ENABLE_PKEYS) - // If we use a pkey pool, we need to tag its metadata with the pkey. Allow the - // function to get access to the pool pointer. - friend void TagGlobalsWithPkey(int pkey); -#endif - - constexpr AddressPoolManager() = default; - ~AddressPoolManager() = default; - - // Populates `stats` if applicable. - // Returns whether `stats` was populated. (They might not be, e.g. - // if PartitionAlloc is wholly unused in this process.) - bool GetStats(AddressSpaceStats* stats); - -#if BUILDFLAG(HAS_64_BIT_POINTERS) - class Pool { - public: - constexpr Pool() = default; - ~Pool() = default; - - Pool(const Pool&) = delete; - Pool& operator=(const Pool&) = delete; - - void Initialize(uintptr_t ptr, size_t length); - bool IsInitialized(); - void Reset(); - - uintptr_t FindChunk(size_t size); - void FreeChunk(uintptr_t address, size_t size); - - bool TryReserveChunk(uintptr_t address, size_t size); - - void GetUsedSuperPages(std::bitset<kMaxSuperPagesInPool>& used); - uintptr_t GetBaseAddress(); - - void GetStats(PoolStats* stats); - - private: - Lock lock_; - - // The bitset stores the allocation state of the address pool. 1 bit per - // super-page: 1 = allocated, 0 = free. - std::bitset<kMaxSuperPagesInPool> alloc_bitset_ PA_GUARDED_BY(lock_); - - // An index of a bit in the bitset before which we know for sure there all - // 1s. This is a best-effort hint in the sense that there still may be lots - // of 1s after this index, but at least we know there is no point in - // starting the search before it. - size_t bit_hint_ PA_GUARDED_BY(lock_) = 0; - - size_t total_bits_ = 0; - uintptr_t address_begin_ = 0; -#if BUILDFLAG(PA_DCHECK_IS_ON) - uintptr_t address_end_ = 0; -#endif - }; - - PA_ALWAYS_INLINE Pool* GetPool(pool_handle handle) { - PA_DCHECK(kNullPoolHandle < handle && handle <= kNumPools); - return &aligned_pools_.pools_[handle - 1]; - } - - // Gets the stats for the pool identified by `handle`, if - // initialized. - void GetPoolStats(pool_handle handle, PoolStats* stats); - - // If pkey support is enabled, we need to pkey-tag the pkey pool (which needs - // to be last). For this, we need to add padding in front of the pools so that - // pkey one starts on a page boundary. - struct { - char pad_[PA_PKEY_ARRAY_PAD_SZ(Pool, kNumPools)] = {}; - Pool pools_[kNumPools]; - char pad_after_[PA_PKEY_FILL_PAGE_SZ(sizeof(Pool))] = {}; - } aligned_pools_ PA_PKEY_ALIGN; - -#endif // BUILDFLAG(HAS_64_BIT_POINTERS) - - static PA_CONSTINIT AddressPoolManager singleton_; -}; - -} // namespace partition_alloc::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_POOL_MANAGER_H_
diff --git a/base/allocator/partition_allocator/address_pool_manager_bitmap.cc b/base/allocator/partition_allocator/address_pool_manager_bitmap.cc deleted file mode 100644 index 92fab78..0000000 --- a/base/allocator/partition_allocator/address_pool_manager_bitmap.cc +++ /dev/null
@@ -1,37 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/address_pool_manager_bitmap.h" - -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" - -#if !BUILDFLAG(HAS_64_BIT_POINTERS) - -namespace partition_alloc::internal { - -namespace { - -Lock g_lock; - -} // namespace - -Lock& AddressPoolManagerBitmap::GetLock() { - return g_lock; -} - -std::bitset<AddressPoolManagerBitmap::kRegularPoolBits> - AddressPoolManagerBitmap::regular_pool_bits_; // GUARDED_BY(GetLock()) -std::bitset<AddressPoolManagerBitmap::kBRPPoolBits> - AddressPoolManagerBitmap::brp_pool_bits_; // GUARDED_BY(GetLock()) -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) -std::array<std::atomic_bool, - AddressPoolManagerBitmap::kAddressSpaceSize / kSuperPageSize> - AddressPoolManagerBitmap::brp_forbidden_super_page_map_; -std::atomic_size_t AddressPoolManagerBitmap::blocklist_hit_count_; -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - -} // namespace partition_alloc::internal - -#endif // !BUILDFLAG(HAS_64_BIT_POINTERS)
diff --git a/base/allocator/partition_allocator/address_pool_manager_bitmap.h b/base/allocator/partition_allocator/address_pool_manager_bitmap.h deleted file mode 100644 index e0f75ae..0000000 --- a/base/allocator/partition_allocator/address_pool_manager_bitmap.h +++ /dev/null
@@ -1,189 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_POOL_MANAGER_BITMAP_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_POOL_MANAGER_BITMAP_H_ - -#include <array> -#include <atomic> -#include <bitset> -#include <limits> - -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "base/allocator/partition_allocator/partition_lock.h" -#include "build/build_config.h" - -#if !BUILDFLAG(HAS_64_BIT_POINTERS) - -namespace partition_alloc { - -namespace internal { - -// AddressPoolManagerBitmap is a set of bitmaps that track whether a given -// address is in a pool that supports BackupRefPtr, or in a pool that doesn't -// support it. All PartitionAlloc allocations must be in either of the pools. -// -// This code is specific to 32-bit systems. -class PA_COMPONENT_EXPORT(PARTITION_ALLOC) AddressPoolManagerBitmap { - public: - static constexpr uint64_t kGiB = 1024 * 1024 * 1024ull; - static constexpr uint64_t kAddressSpaceSize = 4ull * kGiB; - - // For BRP pool, we use partition page granularity to eliminate the guard - // pages from the bitmap at the ends: - // - Eliminating the guard page at the beginning is needed so that pointers - // to the end of an allocation that immediately precede a super page in BRP - // pool don't accidentally fall into that pool. - // - Eliminating the guard page at the end is to ensure that the last page - // of the address space isn't in the BRP pool. This allows using sentinels - // like reinterpret_cast<void*>(-1) without a risk of triggering BRP logic - // on an invalid address. (Note, 64-bit systems don't have this problem as - // the upper half of the address space always belongs to the OS.) - // - // Note, direct map allocations also belong to this pool. The same logic as - // above applies. It is important to note, however, that the granularity used - // here has to be a minimum of partition page size and direct map allocation - // granularity. Since DirectMapAllocationGranularity() is no smaller than - // PageAllocationGranularity(), we don't need to decrease the bitmap - // granularity any further. - static constexpr size_t kBitShiftOfBRPPoolBitmap = PartitionPageShift(); - static constexpr size_t kBytesPer1BitOfBRPPoolBitmap = PartitionPageSize(); - static_assert(kBytesPer1BitOfBRPPoolBitmap == 1 << kBitShiftOfBRPPoolBitmap, - ""); - static constexpr size_t kGuardOffsetOfBRPPoolBitmap = 1; - static constexpr size_t kGuardBitsOfBRPPoolBitmap = 2; - static constexpr size_t kBRPPoolBits = - kAddressSpaceSize / kBytesPer1BitOfBRPPoolBitmap; - - // Regular pool may include both normal bucket and direct map allocations, so - // the bitmap granularity has to be at least as small as - // DirectMapAllocationGranularity(). No need to eliminate guard pages at the - // ends, as this is a BackupRefPtr-specific concern, hence no need to lower - // the granularity to partition page size. - static constexpr size_t kBitShiftOfRegularPoolBitmap = - DirectMapAllocationGranularityShift(); - static constexpr size_t kBytesPer1BitOfRegularPoolBitmap = - DirectMapAllocationGranularity(); - static_assert(kBytesPer1BitOfRegularPoolBitmap == - 1 << kBitShiftOfRegularPoolBitmap, - ""); - static constexpr size_t kRegularPoolBits = - kAddressSpaceSize / kBytesPer1BitOfRegularPoolBitmap; - - // Returns false for nullptr. - static bool IsManagedByRegularPool(uintptr_t address) { - static_assert( - std::numeric_limits<uintptr_t>::max() >> kBitShiftOfRegularPoolBitmap < - regular_pool_bits_.size(), - "The bitmap is too small, will result in unchecked out of bounds " - "accesses."); - // It is safe to read |regular_pool_bits_| without a lock since the caller - // is responsible for guaranteeing that the address is inside a valid - // allocation and the deallocation call won't race with this call. - return PA_TS_UNCHECKED_READ( - regular_pool_bits_)[address >> kBitShiftOfRegularPoolBitmap]; - } - - // Returns false for nullptr. - static bool IsManagedByBRPPool(uintptr_t address) { - static_assert(std::numeric_limits<uintptr_t>::max() >> - kBitShiftOfBRPPoolBitmap < brp_pool_bits_.size(), - "The bitmap is too small, will result in unchecked out of " - "bounds accesses."); - // It is safe to read |brp_pool_bits_| without a lock since the caller - // is responsible for guaranteeing that the address is inside a valid - // allocation and the deallocation call won't race with this call. - return PA_TS_UNCHECKED_READ( - brp_pool_bits_)[address >> kBitShiftOfBRPPoolBitmap]; - } - -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - static void BanSuperPageFromBRPPool(uintptr_t address) { - brp_forbidden_super_page_map_[address >> kSuperPageShift].store( - true, std::memory_order_relaxed); - } - - static bool IsAllowedSuperPageForBRPPool(uintptr_t address) { - // The only potentially dangerous scenario, in which this check is used, is - // when the assignment of the first raw_ptr<T> object for an address - // allocated outside the BRP pool is racing with the allocation of a new - // super page at the same address. We assume that if raw_ptr<T> is being - // initialized with a raw pointer, the associated allocation is "alive"; - // otherwise, the issue should be fixed by rewriting the raw pointer - // variable as raw_ptr<T>. In the worst case, when such a fix is - // impossible, we should just undo the raw pointer -> raw_ptr<T> rewrite of - // the problematic field. If the above assumption holds, the existing - // allocation will prevent us from reserving the super-page region and, - // thus, having the race condition. Since we rely on that external - // synchronization, the relaxed memory ordering should be sufficient. - return !brp_forbidden_super_page_map_[address >> kSuperPageShift].load( - std::memory_order_relaxed); - } - - static void IncrementBlocklistHitCount() { ++blocklist_hit_count_; } -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - - private: - friend class AddressPoolManager; - - static Lock& GetLock(); - - static std::bitset<kRegularPoolBits> regular_pool_bits_ - PA_GUARDED_BY(GetLock()); - static std::bitset<kBRPPoolBits> brp_pool_bits_ PA_GUARDED_BY(GetLock()); -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - static std::array<std::atomic_bool, kAddressSpaceSize / kSuperPageSize> - brp_forbidden_super_page_map_; - static std::atomic_size_t blocklist_hit_count_; -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) -}; - -} // namespace internal - -// Returns false for nullptr. -PA_ALWAYS_INLINE bool IsManagedByPartitionAlloc(uintptr_t address) { - // When ENABLE_BACKUP_REF_PTR_SUPPORT is off, BRP pool isn't used. - // No need to add IsManagedByConfigurablePool, because Configurable Pool - // doesn't exist on 32-bit. -#if !BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - PA_DCHECK(!internal::AddressPoolManagerBitmap::IsManagedByBRPPool(address)); -#endif - return internal::AddressPoolManagerBitmap::IsManagedByRegularPool(address) -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - || internal::AddressPoolManagerBitmap::IsManagedByBRPPool(address) -#endif - ; -} - -// Returns false for nullptr. -PA_ALWAYS_INLINE bool IsManagedByPartitionAllocRegularPool(uintptr_t address) { - return internal::AddressPoolManagerBitmap::IsManagedByRegularPool(address); -} - -// Returns false for nullptr. -PA_ALWAYS_INLINE bool IsManagedByPartitionAllocBRPPool(uintptr_t address) { - return internal::AddressPoolManagerBitmap::IsManagedByBRPPool(address); -} - -// Returns false for nullptr. -PA_ALWAYS_INLINE bool IsManagedByPartitionAllocConfigurablePool( - uintptr_t address) { - // The Configurable Pool is only available on 64-bit builds. - return false; -} - -PA_ALWAYS_INLINE bool IsConfigurablePoolAvailable() { - // The Configurable Pool is only available on 64-bit builds. - return false; -} - -} // namespace partition_alloc - -#endif // !BUILDFLAG(HAS_64_BIT_POINTERS) - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_POOL_MANAGER_BITMAP_H_
diff --git a/base/allocator/partition_allocator/address_pool_manager_types.h b/base/allocator/partition_allocator/address_pool_manager_types.h deleted file mode 100644 index 1c343d3..0000000 --- a/base/allocator/partition_allocator/address_pool_manager_types.h +++ /dev/null
@@ -1,14 +0,0 @@ -// 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 BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_POOL_MANAGER_TYPES_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_POOL_MANAGER_TYPES_H_ - -namespace partition_alloc::internal { - -enum pool_handle : unsigned; - -} // namespace partition_alloc::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_POOL_MANAGER_TYPES_H_
diff --git a/base/allocator/partition_allocator/address_pool_manager_unittest.cc b/base/allocator/partition_allocator/address_pool_manager_unittest.cc deleted file mode 100644 index 21be980..0000000 --- a/base/allocator/partition_allocator/address_pool_manager_unittest.cc +++ /dev/null
@@ -1,405 +0,0 @@ -// 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. - -#include "base/allocator/partition_allocator/address_pool_manager.h" - -#include <cstdint> - -#include "base/allocator/partition_allocator/address_space_stats.h" -#include "base/allocator/partition_allocator/page_allocator.h" -#include "base/allocator/partition_allocator/partition_alloc_base/bits.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace partition_alloc::internal { - -class AddressSpaceStatsDumperForTesting final : public AddressSpaceStatsDumper { - public: - AddressSpaceStatsDumperForTesting() = default; - ~AddressSpaceStatsDumperForTesting() = default; - - void DumpStats( - const partition_alloc::AddressSpaceStats* address_space_stats) override { - regular_pool_usage_ = address_space_stats->regular_pool_stats.usage; -#if BUILDFLAG(HAS_64_BIT_POINTERS) - regular_pool_largest_reservation_ = - address_space_stats->regular_pool_stats.largest_available_reservation; -#endif -#if !BUILDFLAG(HAS_64_BIT_POINTERS) && BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - blocklist_size_ = address_space_stats->blocklist_size; -#endif - } - - size_t regular_pool_usage_ = 0; - size_t regular_pool_largest_reservation_ = 0; - size_t blocklist_size_ = 0; -}; - -#if BUILDFLAG(HAS_64_BIT_POINTERS) - -class AddressPoolManagerForTesting : public AddressPoolManager { - public: - AddressPoolManagerForTesting() = default; - ~AddressPoolManagerForTesting() = default; -}; - -class PartitionAllocAddressPoolManagerTest : public testing::Test { - protected: - PartitionAllocAddressPoolManagerTest() = default; - ~PartitionAllocAddressPoolManagerTest() override = default; - - void SetUp() override { - manager_ = std::make_unique<AddressPoolManagerForTesting>(); - base_address_ = - AllocPages(kPoolSize, kSuperPageSize, - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kInaccessible), - PageTag::kPartitionAlloc); - ASSERT_TRUE(base_address_); - manager_->Add(kRegularPoolHandle, base_address_, kPoolSize); - pool_ = kRegularPoolHandle; - } - - void TearDown() override { - manager_->Remove(pool_); - FreePages(base_address_, kPoolSize); - manager_.reset(); - } - - AddressPoolManager* GetAddressPoolManager() { return manager_.get(); } - - static constexpr size_t kPoolSize = kPoolMaxSize; - static constexpr size_t kPageCnt = kPoolSize / kSuperPageSize; - - std::unique_ptr<AddressPoolManagerForTesting> manager_; - uintptr_t base_address_; - pool_handle pool_; -}; - -TEST_F(PartitionAllocAddressPoolManagerTest, TooLargePool) { - uintptr_t base_addr = 0x4200000; - const pool_handle extra_pool = static_cast<pool_handle>(2u); - static_assert(kNumPools >= 2); - - EXPECT_DEATH_IF_SUPPORTED( - GetAddressPoolManager()->Add(extra_pool, base_addr, - kPoolSize + kSuperPageSize), - ""); -} - -TEST_F(PartitionAllocAddressPoolManagerTest, ManyPages) { - EXPECT_EQ( - GetAddressPoolManager()->Reserve(pool_, 0, kPageCnt * kSuperPageSize), - base_address_); - EXPECT_EQ(GetAddressPoolManager()->Reserve(pool_, 0, kSuperPageSize), 0u); - GetAddressPoolManager()->UnreserveAndDecommit(pool_, base_address_, - kPageCnt * kSuperPageSize); - - EXPECT_EQ( - GetAddressPoolManager()->Reserve(pool_, 0, kPageCnt * kSuperPageSize), - base_address_); - GetAddressPoolManager()->UnreserveAndDecommit(pool_, base_address_, - kPageCnt * kSuperPageSize); -} - -TEST_F(PartitionAllocAddressPoolManagerTest, PagesFragmented) { - uintptr_t addrs[kPageCnt]; - for (size_t i = 0; i < kPageCnt; ++i) { - addrs[i] = GetAddressPoolManager()->Reserve(pool_, 0, kSuperPageSize); - EXPECT_EQ(addrs[i], base_address_ + i * kSuperPageSize); - } - EXPECT_EQ(GetAddressPoolManager()->Reserve(pool_, 0, kSuperPageSize), 0u); - // Free other other super page, so that we have plenty of free space, but none - // of the empty spaces can fit 2 super pages. - for (size_t i = 1; i < kPageCnt; i += 2) { - GetAddressPoolManager()->UnreserveAndDecommit(pool_, addrs[i], - kSuperPageSize); - } - EXPECT_EQ(GetAddressPoolManager()->Reserve(pool_, 0, 2 * kSuperPageSize), 0u); - // Reserve freed super pages back, so that there are no free ones. - for (size_t i = 1; i < kPageCnt; i += 2) { - addrs[i] = GetAddressPoolManager()->Reserve(pool_, 0, kSuperPageSize); - EXPECT_EQ(addrs[i], base_address_ + i * kSuperPageSize); - } - EXPECT_EQ(GetAddressPoolManager()->Reserve(pool_, 0, kSuperPageSize), 0u); - // Lastly, clean up. - for (uintptr_t addr : addrs) { - GetAddressPoolManager()->UnreserveAndDecommit(pool_, addr, kSuperPageSize); - } -} - -TEST_F(PartitionAllocAddressPoolManagerTest, GetUsedSuperpages) { - uintptr_t addrs[kPageCnt]; - for (size_t i = 0; i < kPageCnt; ++i) { - addrs[i] = GetAddressPoolManager()->Reserve(pool_, 0, kSuperPageSize); - EXPECT_EQ(addrs[i], base_address_ + i * kSuperPageSize); - } - EXPECT_EQ(GetAddressPoolManager()->Reserve(pool_, 0, kSuperPageSize), 0u); - - std::bitset<kMaxSuperPagesInPool> used_super_pages; - GetAddressPoolManager()->GetPoolUsedSuperPages(pool_, used_super_pages); - - // We expect every bit to be set. - for (size_t i = 0; i < kPageCnt; ++i) { - ASSERT_TRUE(used_super_pages.test(i)); - } - - // Free every other super page, so that we have plenty of free space, but none - // of the empty spaces can fit 2 super pages. - for (size_t i = 1; i < kPageCnt; i += 2) { - GetAddressPoolManager()->UnreserveAndDecommit(pool_, addrs[i], - kSuperPageSize); - } - - EXPECT_EQ(GetAddressPoolManager()->Reserve(pool_, 0, 2 * kSuperPageSize), 0u); - - GetAddressPoolManager()->GetPoolUsedSuperPages(pool_, used_super_pages); - - // We expect every other bit to be set. - for (size_t i = 0; i < kPageCnt; i++) { - if (i % 2 == 0) { - ASSERT_TRUE(used_super_pages.test(i)); - } else { - ASSERT_FALSE(used_super_pages.test(i)); - } - } - - // Free the even numbered super pages. - for (size_t i = 0; i < kPageCnt; i += 2) { - GetAddressPoolManager()->UnreserveAndDecommit(pool_, addrs[i], - kSuperPageSize); - } - - // Finally check to make sure all bits are zero in the used superpage bitset. - GetAddressPoolManager()->GetPoolUsedSuperPages(pool_, used_super_pages); - - for (size_t i = 0; i < kPageCnt; i++) { - ASSERT_FALSE(used_super_pages.test(i)); - } -} - -TEST_F(PartitionAllocAddressPoolManagerTest, IrregularPattern) { - uintptr_t a1 = GetAddressPoolManager()->Reserve(pool_, 0, kSuperPageSize); - EXPECT_EQ(a1, base_address_); - uintptr_t a2 = GetAddressPoolManager()->Reserve(pool_, 0, 2 * kSuperPageSize); - EXPECT_EQ(a2, base_address_ + 1 * kSuperPageSize); - uintptr_t a3 = GetAddressPoolManager()->Reserve(pool_, 0, 3 * kSuperPageSize); - EXPECT_EQ(a3, base_address_ + 3 * kSuperPageSize); - uintptr_t a4 = GetAddressPoolManager()->Reserve(pool_, 0, 4 * kSuperPageSize); - EXPECT_EQ(a4, base_address_ + 6 * kSuperPageSize); - uintptr_t a5 = GetAddressPoolManager()->Reserve(pool_, 0, 5 * kSuperPageSize); - EXPECT_EQ(a5, base_address_ + 10 * kSuperPageSize); - - GetAddressPoolManager()->UnreserveAndDecommit(pool_, a4, 4 * kSuperPageSize); - uintptr_t a6 = GetAddressPoolManager()->Reserve(pool_, 0, 6 * kSuperPageSize); - EXPECT_EQ(a6, base_address_ + 15 * kSuperPageSize); - - GetAddressPoolManager()->UnreserveAndDecommit(pool_, a5, 5 * kSuperPageSize); - uintptr_t a7 = GetAddressPoolManager()->Reserve(pool_, 0, 7 * kSuperPageSize); - EXPECT_EQ(a7, base_address_ + 6 * kSuperPageSize); - uintptr_t a8 = GetAddressPoolManager()->Reserve(pool_, 0, 3 * kSuperPageSize); - EXPECT_EQ(a8, base_address_ + 21 * kSuperPageSize); - uintptr_t a9 = GetAddressPoolManager()->Reserve(pool_, 0, 2 * kSuperPageSize); - EXPECT_EQ(a9, base_address_ + 13 * kSuperPageSize); - - GetAddressPoolManager()->UnreserveAndDecommit(pool_, a7, 7 * kSuperPageSize); - GetAddressPoolManager()->UnreserveAndDecommit(pool_, a9, 2 * kSuperPageSize); - GetAddressPoolManager()->UnreserveAndDecommit(pool_, a6, 6 * kSuperPageSize); - uintptr_t a10 = - GetAddressPoolManager()->Reserve(pool_, 0, 15 * kSuperPageSize); - EXPECT_EQ(a10, base_address_ + 6 * kSuperPageSize); - - // Clean up. - GetAddressPoolManager()->UnreserveAndDecommit(pool_, a1, kSuperPageSize); - GetAddressPoolManager()->UnreserveAndDecommit(pool_, a2, 2 * kSuperPageSize); - GetAddressPoolManager()->UnreserveAndDecommit(pool_, a3, 3 * kSuperPageSize); - GetAddressPoolManager()->UnreserveAndDecommit(pool_, a8, 3 * kSuperPageSize); - GetAddressPoolManager()->UnreserveAndDecommit(pool_, a10, - 15 * kSuperPageSize); -} - -TEST_F(PartitionAllocAddressPoolManagerTest, DecommittedDataIsErased) { - uintptr_t address = - GetAddressPoolManager()->Reserve(pool_, 0, kSuperPageSize); - ASSERT_TRUE(address); - RecommitSystemPages(address, kSuperPageSize, - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kReadWrite), - PageAccessibilityDisposition::kRequireUpdate); - - memset(reinterpret_cast<void*>(address), 42, kSuperPageSize); - GetAddressPoolManager()->UnreserveAndDecommit(pool_, address, kSuperPageSize); - - uintptr_t address2 = - GetAddressPoolManager()->Reserve(pool_, 0, kSuperPageSize); - ASSERT_EQ(address, address2); - RecommitSystemPages(address2, kSuperPageSize, - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kReadWrite), - PageAccessibilityDisposition::kRequireUpdate); - - uint32_t sum = 0; - for (size_t i = 0; i < kSuperPageSize; i++) { - sum += reinterpret_cast<uint8_t*>(address2)[i]; - } - EXPECT_EQ(0u, sum) << sum / 42 << " bytes were not zeroed"; - - GetAddressPoolManager()->UnreserveAndDecommit(pool_, address2, - kSuperPageSize); -} - -TEST_F(PartitionAllocAddressPoolManagerTest, RegularPoolUsageChanges) { - AddressSpaceStatsDumperForTesting dumper{}; - - GetAddressPoolManager()->DumpStats(&dumper); - ASSERT_EQ(dumper.regular_pool_usage_, 0ull); - ASSERT_EQ(dumper.regular_pool_largest_reservation_, kPageCnt); - - // Bisect the pool by reserving a super page in the middle. - const uintptr_t midpoint_address = - base_address_ + (kPageCnt / 2) * kSuperPageSize; - ASSERT_EQ( - GetAddressPoolManager()->Reserve(pool_, midpoint_address, kSuperPageSize), - midpoint_address); - - GetAddressPoolManager()->DumpStats(&dumper); - ASSERT_EQ(dumper.regular_pool_usage_, 1ull); - ASSERT_EQ(dumper.regular_pool_largest_reservation_, kPageCnt / 2); - - GetAddressPoolManager()->UnreserveAndDecommit(pool_, midpoint_address, - kSuperPageSize); - - GetAddressPoolManager()->DumpStats(&dumper); - ASSERT_EQ(dumper.regular_pool_usage_, 0ull); - ASSERT_EQ(dumper.regular_pool_largest_reservation_, kPageCnt); -} - -#else // BUILDFLAG(HAS_64_BIT_POINTERS) - -TEST(PartitionAllocAddressPoolManagerTest, IsManagedByRegularPool) { - constexpr size_t kAllocCount = 8; - static const size_t kNumPages[kAllocCount] = {1, 4, 7, 8, 13, 16, 31, 60}; - uintptr_t addrs[kAllocCount]; - for (size_t i = 0; i < kAllocCount; ++i) { - addrs[i] = AddressPoolManager::GetInstance().Reserve( - kRegularPoolHandle, 0, - AddressPoolManagerBitmap::kBytesPer1BitOfRegularPoolBitmap * - kNumPages[i]); - EXPECT_TRUE(addrs[i]); - EXPECT_TRUE(!(addrs[i] & kSuperPageOffsetMask)); - AddressPoolManager::GetInstance().MarkUsed( - kRegularPoolHandle, addrs[i], - AddressPoolManagerBitmap::kBytesPer1BitOfRegularPoolBitmap * - kNumPages[i]); - } - for (size_t i = 0; i < kAllocCount; ++i) { - uintptr_t address = addrs[i]; - size_t num_pages = - base::bits::AlignUp( - kNumPages[i] * - AddressPoolManagerBitmap::kBytesPer1BitOfRegularPoolBitmap, - kSuperPageSize) / - AddressPoolManagerBitmap::kBytesPer1BitOfRegularPoolBitmap; - for (size_t j = 0; j < num_pages; ++j) { - if (j < kNumPages[i]) { - EXPECT_TRUE(AddressPoolManager::IsManagedByRegularPool(address)); - } else { - EXPECT_FALSE(AddressPoolManager::IsManagedByRegularPool(address)); - } - EXPECT_FALSE(AddressPoolManager::IsManagedByBRPPool(address)); - address += AddressPoolManagerBitmap::kBytesPer1BitOfRegularPoolBitmap; - } - } - for (size_t i = 0; i < kAllocCount; ++i) { - AddressPoolManager::GetInstance().MarkUnused( - kRegularPoolHandle, addrs[i], - AddressPoolManagerBitmap::kBytesPer1BitOfRegularPoolBitmap * - kNumPages[i]); - AddressPoolManager::GetInstance().UnreserveAndDecommit( - kRegularPoolHandle, addrs[i], - AddressPoolManagerBitmap::kBytesPer1BitOfRegularPoolBitmap * - kNumPages[i]); - EXPECT_FALSE(AddressPoolManager::IsManagedByRegularPool(addrs[i])); - EXPECT_FALSE(AddressPoolManager::IsManagedByBRPPool(addrs[i])); - } -} - -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) -TEST(PartitionAllocAddressPoolManagerTest, IsManagedByBRPPool) { - constexpr size_t kAllocCount = 4; - // Totally (1+3+7+11) * 2MB = 44MB allocation - static const size_t kNumPages[kAllocCount] = {1, 3, 7, 11}; - uintptr_t addrs[kAllocCount]; - for (size_t i = 0; i < kAllocCount; ++i) { - addrs[i] = AddressPoolManager::GetInstance().Reserve( - kBRPPoolHandle, 0, kSuperPageSize * kNumPages[i]); - EXPECT_TRUE(addrs[i]); - EXPECT_TRUE(!(addrs[i] & kSuperPageOffsetMask)); - AddressPoolManager::GetInstance().MarkUsed(kBRPPoolHandle, addrs[i], - kSuperPageSize * kNumPages[i]); - } - - constexpr size_t first_guard_size = - AddressPoolManagerBitmap::kBytesPer1BitOfBRPPoolBitmap * - AddressPoolManagerBitmap::kGuardOffsetOfBRPPoolBitmap; - constexpr size_t last_guard_size = - AddressPoolManagerBitmap::kBytesPer1BitOfBRPPoolBitmap * - (AddressPoolManagerBitmap::kGuardBitsOfBRPPoolBitmap - - AddressPoolManagerBitmap::kGuardOffsetOfBRPPoolBitmap); - - for (size_t i = 0; i < kAllocCount; ++i) { - uintptr_t address = addrs[i]; - size_t num_allocated_size = kNumPages[i] * kSuperPageSize; - size_t num_system_pages = num_allocated_size / SystemPageSize(); - for (size_t j = 0; j < num_system_pages; ++j) { - size_t offset = address - addrs[i]; - if (offset < first_guard_size || - offset >= (num_allocated_size - last_guard_size)) { - EXPECT_FALSE(AddressPoolManager::IsManagedByBRPPool(address)); - } else { - EXPECT_TRUE(AddressPoolManager::IsManagedByBRPPool(address)); - } - EXPECT_FALSE(AddressPoolManager::IsManagedByRegularPool(address)); - address += SystemPageSize(); - } - } - for (size_t i = 0; i < kAllocCount; ++i) { - AddressPoolManager::GetInstance().MarkUnused(kBRPPoolHandle, addrs[i], - kSuperPageSize * kNumPages[i]); - AddressPoolManager::GetInstance().UnreserveAndDecommit( - kBRPPoolHandle, addrs[i], kSuperPageSize * kNumPages[i]); - EXPECT_FALSE(AddressPoolManager::IsManagedByRegularPool(addrs[i])); - EXPECT_FALSE(AddressPoolManager::IsManagedByBRPPool(addrs[i])); - } -} -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - -TEST(PartitionAllocAddressPoolManagerTest, RegularPoolUsageChanges) { - AddressSpaceStatsDumperForTesting dumper{}; - AddressPoolManager::GetInstance().DumpStats(&dumper); - const size_t usage_before = dumper.regular_pool_usage_; - - const uintptr_t address = AddressPoolManager::GetInstance().Reserve( - kRegularPoolHandle, 0, kSuperPageSize); - ASSERT_TRUE(address); - AddressPoolManager::GetInstance().MarkUsed(kRegularPoolHandle, address, - kSuperPageSize); - - AddressPoolManager::GetInstance().DumpStats(&dumper); - EXPECT_GT(dumper.regular_pool_usage_, usage_before); - - AddressPoolManager::GetInstance().MarkUnused(kRegularPoolHandle, address, - kSuperPageSize); - AddressPoolManager::GetInstance().UnreserveAndDecommit( - kRegularPoolHandle, address, kSuperPageSize); - - AddressPoolManager::GetInstance().DumpStats(&dumper); - EXPECT_EQ(dumper.regular_pool_usage_, usage_before); -} - -#endif // BUILDFLAG(HAS_64_BIT_POINTERS) - -} // namespace partition_alloc::internal
diff --git a/base/allocator/partition_allocator/address_space_randomization.cc b/base/allocator/partition_allocator/address_space_randomization.cc deleted file mode 100644 index 60fcf94..0000000 --- a/base/allocator/partition_allocator/address_space_randomization.cc +++ /dev/null
@@ -1,51 +0,0 @@ -// Copyright 2014 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/address_space_randomization.h" - -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/random.h" -#include "build/build_config.h" - -#if BUILDFLAG(IS_WIN) -#include <windows.h> -#endif - -namespace partition_alloc { - -uintptr_t GetRandomPageBase() { - uintptr_t random = static_cast<uintptr_t>(internal::RandomValue()); - -#if BUILDFLAG(HAS_64_BIT_POINTERS) - random <<= 32ULL; - random |= static_cast<uintptr_t>(internal::RandomValue()); - - // The ASLRMask() and ASLROffset() constants will be suitable for the - // OS and build configuration. - random &= internal::ASLRMask(); - random += internal::ASLROffset(); -#else // BUILDFLAG(HAS_64_BIT_POINTERS) -#if BUILDFLAG(IS_WIN) - // On win32 host systems the randomization plus huge alignment causes - // excessive fragmentation. Plus most of these systems lack ASLR, so the - // randomization isn't buying anything. In that case we just skip it. - // TODO(palmer): Just dump the randomization when HE-ASLR is present. - static BOOL is_wow64 = -1; - if (is_wow64 == -1 && !IsWow64Process(GetCurrentProcess(), &is_wow64)) { - is_wow64 = FALSE; - } - if (!is_wow64) { - return 0; - } -#endif // BUILDFLAG(IS_WIN) - random &= internal::ASLRMask(); - random += internal::ASLROffset(); -#endif // BUILDFLAG(HAS_64_BIT_POINTERS) - - PA_DCHECK(!(random & internal::PageAllocationGranularityOffsetMask())); - return random; -} - -} // namespace partition_alloc
diff --git a/base/allocator/partition_allocator/address_space_randomization.h b/base/allocator/partition_allocator/address_space_randomization.h deleted file mode 100644 index cc69f0d..0000000 --- a/base/allocator/partition_allocator/address_space_randomization.h +++ /dev/null
@@ -1,286 +0,0 @@ -// Copyright 2014 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_SPACE_RANDOMIZATION_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_SPACE_RANDOMIZATION_H_ - -#include <cstdint> - -#include "base/allocator/partition_allocator/page_allocator_constants.h" -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "build/build_config.h" - -namespace partition_alloc { - -// Calculates a random preferred mapping address. In calculating an address, we -// balance good ASLR against not fragmenting the address space too badly. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) uintptr_t GetRandomPageBase(); - -namespace internal { - -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t -AslrAddress(uintptr_t mask) { - return mask & PageAllocationGranularityBaseMask(); -} -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t -AslrMask(uintptr_t bits) { - return AslrAddress((1ULL << bits) - 1ULL); -} - -// Turn off formatting, because the thicket of nested ifdefs below is -// incomprehensible without indentation. It is also incomprehensible with -// indentation, but the only other option is a combinatorial explosion of -// *_{win,linux,mac,foo}_{32,64}.h files. -// -// clang-format off - -#if defined(ARCH_CPU_64_BITS) - - #if defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - - // We shouldn't allocate system pages at all for sanitizer builds. However, - // we do, and if random hint addresses interfere with address ranges - // hard-coded in those tools, bad things happen. This address range is - // copied from TSAN source but works with all tools. See - // https://crbug.com/539863. - PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t - ASLRMask() { - return AslrAddress(0x007fffffffffULL); - } - PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t - ASLROffset() { - return AslrAddress(0x7e8000000000ULL); - } - - #elif BUILDFLAG(IS_WIN) - - // Windows 8.10 and newer support the full 48 bit address range. Since - // ASLROffset() is non-zero and may cause a carry, use 47 bit masks. See - // http://www.alex-ionescu.com/?p=246 - PA_ALWAYS_INLINE constexpr uintptr_t ASLRMask() { - return AslrMask(47); - } - // Try not to map pages into the range where Windows loads DLLs by default. - PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() { - return 0x80000000ULL; - } - - #elif BUILDFLAG(IS_APPLE) - - // macOS as of 10.12.5 does not clean up entries in page map levels 3/4 - // [PDP/PML4] created from mmap or mach_vm_allocate, even after the region - // is destroyed. Using a virtual address space that is too large causes a - // leak of about 1 wired [can never be paged out] page per call to mmap. The - // page is only reclaimed when the process is killed. Confine the hint to a - // 39-bit section of the virtual address space. - // - // This implementation adapted from - // https://chromium-review.googlesource.com/c/v8/v8/+/557958. The difference - // is that here we clamp to 39 bits, not 32. - // - // TODO(crbug.com/738925): Remove this limitation if/when the macOS behavior - // changes. - PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t - ASLRMask() { - return AslrMask(38); - } - PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t - ASLROffset() { - // Be careful, there is a zone where macOS will not map memory, at least - // on ARM64. From an ARM64 machine running 12.3, the range seems to be - // [0x1000000000, 0x7000000000). Make sure that the range we use is - // outside these bounds. In 12.3, there is a reserved area between - // MACH_VM_MIN_GPU_CARVEOUT_ADDRESS and MACH_VM_MAX_GPU_CARVEOUT_ADDRESS, - // which is reserved on ARM64. See these constants in XNU's source code - // for details (xnu-8019.80.24/osfmk/mach/arm/vm_param.h). - return AslrAddress(0x10000000000ULL); - } - - #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) - - #if defined(ARCH_CPU_X86_64) - - // Linux (and macOS) support the full 47-bit user space of x64 processors. - // Use only 46 to allow the kernel a chance to fulfill the request. - PA_ALWAYS_INLINE constexpr uintptr_t ASLRMask() { - return AslrMask(46); - } - PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() { - return AslrAddress(0); - } - - #elif defined(ARCH_CPU_ARM64) - - #if BUILDFLAG(IS_ANDROID) - - // Restrict the address range on Android to avoid a large performance - // regression in single-process WebViews. See https://crbug.com/837640. - PA_ALWAYS_INLINE constexpr uintptr_t ASLRMask() { - return AslrMask(30); - } - PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() { - return AslrAddress(0x20000000ULL); - } - - #elif BUILDFLAG(IS_LINUX) - - // Linux on arm64 can use 39, 42, 48, or 52-bit user space, depending on - // page size and number of levels of translation pages used. We use - // 39-bit as base as all setups should support this, lowered to 38-bit - // as ASLROffset() could cause a carry. - PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t - ASLRMask() { - return AslrMask(38); - } - PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t - ASLROffset() { - return AslrAddress(0x1000000000ULL); - } - - #else - - // ARM64 on Linux has 39-bit user space. Use 38 bits since ASLROffset() - // could cause a carry. - PA_ALWAYS_INLINE constexpr uintptr_t ASLRMask() { - return AslrMask(38); - } - PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() { - return AslrAddress(0x1000000000ULL); - } - - #endif - - #elif defined(ARCH_CPU_PPC64) - - #if BUILDFLAG(IS_AIX) - - // AIX has 64 bits of virtual addressing, but we limit the address range - // to (a) minimize segment lookaside buffer (SLB) misses; and (b) use - // extra address space to isolate the mmap regions. - PA_ALWAYS_INLINE constexpr uintptr_t ASLRMask() { - return AslrMask(30); - } - PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() { - return AslrAddress(0x400000000000ULL); - } - - #elif defined(ARCH_CPU_BIG_ENDIAN) - - // Big-endian Linux PPC has 44 bits of virtual addressing. Use 42. - PA_ALWAYS_INLINE constexpr uintptr_t ASLRMask() { - return AslrMask(42); - } - PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() { - return AslrAddress(0); - } - - #else // !BUILDFLAG(IS_AIX) && !defined(ARCH_CPU_BIG_ENDIAN) - - // Little-endian Linux PPC has 48 bits of virtual addressing. Use 46. - PA_ALWAYS_INLINE constexpr uintptr_t ASLRMask() { - return AslrMask(46); - } - PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() { - return AslrAddress(0); - } - - #endif // !BUILDFLAG(IS_AIX) && !defined(ARCH_CPU_BIG_ENDIAN) - - #elif defined(ARCH_CPU_S390X) - - // Linux on Z uses bits 22 - 32 for Region Indexing, which translates to - // 42 bits of virtual addressing. Truncate to 40 bits to allow kernel a - // chance to fulfill the request. - PA_ALWAYS_INLINE constexpr uintptr_t ASLRMask() { - return AslrMask(40); - } - PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() { - return AslrAddress(0); - } - - #elif defined(ARCH_CPU_S390) - - // 31 bits of virtual addressing. Truncate to 29 bits to allow the kernel - // a chance to fulfill the request. - PA_ALWAYS_INLINE constexpr uintptr_t ASLRMask() { - return AslrMask(29); - } - PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() { - return AslrAddress(0); - } - - #else // !defined(ARCH_CPU_X86_64) && !defined(ARCH_CPU_PPC64) && - // !defined(ARCH_CPU_S390X) && !defined(ARCH_CPU_S390) - - // For all other POSIX variants, use 30 bits. - PA_ALWAYS_INLINE constexpr uintptr_t ASLRMask() { - return AslrMask(30); - } - - #if BUILDFLAG(IS_SOLARIS) - - // For our Solaris/illumos mmap hint, we pick a random address in the - // bottom half of the top half of the address space (that is, the third - // quarter). Because we do not MAP_FIXED, this will be treated only as a - // hint -- the system will not fail to mmap because something else - // happens to already be mapped at our random address. We deliberately - // set the hint high enough to get well above the system's break (that - // is, the heap); Solaris and illumos will try the hint and if that - // fails allocate as if there were no hint at all. The high hint - // prevents the break from getting hemmed in at low values, ceding half - // of the address space to the system heap. - PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() { - return AslrAddress(0x80000000ULL); - } - - #elif BUILDFLAG(IS_AIX) - - // The range 0x30000000 - 0xD0000000 is available on AIX; choose the - // upper range. - PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() { - return AslrAddress(0x90000000ULL); - } - - #else // !BUILDFLAG(IS_SOLARIS) && !BUILDFLAG(IS_AIX) - - // The range 0x20000000 - 0x60000000 is relatively unpopulated across a - // variety of ASLR modes (PAE kernel, NX compat mode, etc) and on macOS - // 10.6 and 10.7. - PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() { - return AslrAddress(0x20000000ULL); - } - - #endif // !BUILDFLAG(IS_SOLARIS) && !BUILDFLAG(IS_AIX) - - #endif // !defined(ARCH_CPU_X86_64) && !defined(ARCH_CPU_PPC64) && - // !defined(ARCH_CPU_S390X) && !defined(ARCH_CPU_S390) - - #endif // BUILDFLAG(IS_POSIX) - -#elif defined(ARCH_CPU_32_BITS) - - // This is a good range on 32-bit Windows and Android (the only platforms on - // which we support 32-bitness). Allocates in the 0.5 - 1.5 GiB region. There - // is no issue with carries here. - PA_ALWAYS_INLINE constexpr uintptr_t ASLRMask() { - return AslrMask(30); - } - PA_ALWAYS_INLINE constexpr uintptr_t ASLROffset() { - return AslrAddress(0x20000000ULL); - } - -#else - - #error Please tell us about your exotic hardware! Sounds interesting. - -#endif // defined(ARCH_CPU_32_BITS) - -// clang-format on - -} // namespace internal - -} // namespace partition_alloc - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_SPACE_RANDOMIZATION_H_
diff --git a/base/allocator/partition_allocator/address_space_randomization_unittest.cc b/base/allocator/partition_allocator/address_space_randomization_unittest.cc deleted file mode 100644 index 9361c38..0000000 --- a/base/allocator/partition_allocator/address_space_randomization_unittest.cc +++ /dev/null
@@ -1,283 +0,0 @@ -// Copyright 2017 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/address_space_randomization.h" - -#include <cstdint> -#include <vector> - -#include "base/allocator/partition_allocator/page_allocator.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/random.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -#if BUILDFLAG(IS_WIN) -#include <windows.h> -#include "base/win/windows_version.h" -#endif - -namespace partition_alloc { - -namespace { - -uintptr_t GetMask() { - uintptr_t mask = internal::ASLRMask(); -#if defined(ARCH_CPU_64_BITS) -#elif defined(ARCH_CPU_32_BITS) -#if BUILDFLAG(IS_WIN) - BOOL is_wow64 = FALSE; - if (!IsWow64Process(GetCurrentProcess(), &is_wow64)) { - is_wow64 = FALSE; - } - if (!is_wow64) { - mask = 0; - } -#endif // BUILDFLAG(IS_WIN) -#endif // defined(ARCH_CPU_32_BITS) - return mask; -} - -const size_t kSamples = 100; - -uintptr_t GetAddressBits() { - return GetRandomPageBase(); -} - -uintptr_t GetRandomBits() { - return GetAddressBits() - internal::ASLROffset(); -} - -} // namespace - -// Configurations without ASLR are tested here. -TEST(PartitionAllocAddressSpaceRandomizationTest, DisabledASLR) { - uintptr_t mask = GetMask(); - if (!mask) { -#if BUILDFLAG(IS_WIN) && defined(ARCH_CPU_32_BITS) - // ASLR should be turned off on 32-bit Windows. - EXPECT_EQ(0u, GetRandomPageBase()); -#else - // Otherwise, 0 is very unexpected. - EXPECT_NE(0u, GetRandomPageBase()); -#endif - } -} - -TEST(PartitionAllocAddressSpaceRandomizationTest, Alignment) { - uintptr_t mask = GetMask(); - if (!mask) { - return; - } - - for (size_t i = 0; i < kSamples; ++i) { - uintptr_t address = GetAddressBits(); - EXPECT_EQ(0ULL, - (address & internal::PageAllocationGranularityOffsetMask())); - } -} - -TEST(PartitionAllocAddressSpaceRandomizationTest, Range) { - uintptr_t mask = GetMask(); - if (!mask) { - return; - } - - uintptr_t min = internal::ASLROffset(); - uintptr_t max = internal::ASLROffset() + internal::ASLRMask(); - for (size_t i = 0; i < kSamples; ++i) { - uintptr_t address = GetAddressBits(); - EXPECT_LE(min, address); - EXPECT_GE(max + mask, address); - } -} - -TEST(PartitionAllocAddressSpaceRandomizationTest, Predictable) { - uintptr_t mask = GetMask(); - if (!mask) { - return; - } - - const uint64_t kInitialSeed = 0xfeed5eedULL; - SetMmapSeedForTesting(kInitialSeed); - - std::vector<uintptr_t> sequence; - for (size_t i = 0; i < kSamples; ++i) { - sequence.push_back(GetRandomPageBase()); - } - - SetMmapSeedForTesting(kInitialSeed); - - for (size_t i = 0; i < kSamples; ++i) { - EXPECT_EQ(GetRandomPageBase(), sequence[i]); - } -} - -// This randomness test is adapted from V8's PRNG tests. - -// Chi squared for getting m 0s out of n bits. -double ChiSquared(int m, int n) { - double ys_minus_np1 = (m - n / 2.0); - double chi_squared_1 = ys_minus_np1 * ys_minus_np1 * 2.0 / n; - double ys_minus_np2 = ((n - m) - n / 2.0); - double chi_squared_2 = ys_minus_np2 * ys_minus_np2 * 2.0 / n; - return chi_squared_1 + chi_squared_2; -} - -// Test for correlations between recent bits from the PRNG, or bits that are -// biased. -void RandomBitCorrelation(int random_bit) { - uintptr_t mask = GetMask(); - if ((mask & (1ULL << random_bit)) == 0) { - return; // bit is always 0. - } - -#if BUILDFLAG(PA_DCHECK_IS_ON) - // Do fewer checks when BUILDFLAG(PA_DCHECK_IS_ON). Exercized code only - // changes when the random number generator does, which should be almost - // never. However it's expensive to run all the tests. So keep iterations - // faster for local development builds, while having the stricter version run - // on official build testers. - constexpr int kHistory = 2; - constexpr int kRepeats = 1000; -#else - constexpr int kHistory = 8; - constexpr int kRepeats = 10000; -#endif - constexpr int kPointerBits = 8 * sizeof(void*); - uintptr_t history[kHistory]; - // The predictor bit is either constant 0 or 1, or one of the bits from the - // history. - for (int predictor_bit = -2; predictor_bit < kPointerBits; predictor_bit++) { - // The predicted bit is one of the bits from the PRNG. - for (int ago = 0; ago < kHistory; ago++) { - // We don't want to check whether each bit predicts itself. - if (ago == 0 && predictor_bit == random_bit) { - continue; - } - - // Enter the new random value into the history. - for (int i = ago; i >= 0; i--) { - history[i] = GetRandomBits(); - } - - // Find out how many of the bits are the same as the prediction bit. - int m = 0; - for (int i = 0; i < kRepeats; i++) { - uintptr_t random = GetRandomBits(); - for (int j = ago - 1; j >= 0; j--) { - history[j + 1] = history[j]; - } - history[0] = random; - - int predicted; - if (predictor_bit >= 0) { - predicted = (history[ago] >> predictor_bit) & 1; - } else { - predicted = predictor_bit == -2 ? 0 : 1; - } - int bit = (random >> random_bit) & 1; - if (bit == predicted) { - m++; - } - } - - // Chi squared analysis for k = 2 (2, states: same/not-same) and one - // degree of freedom (k - 1). - double chi_squared = ChiSquared(m, kRepeats); - // For k=2 probability of Chi^2 < 35 is p=3.338e-9. This condition is - // tested ~19000 times, so probability of it failing randomly per one - // base_unittests run is (1 - (1 - p) ^ 19000) ~= 6e-5. - PA_CHECK(chi_squared <= 35.0); - // If the predictor bit is a fixed 0 or 1 then it makes no sense to - // repeat the test with a different age. - if (predictor_bit < 0) { - break; - } - } - } -} - -// Tests are fairly slow, so give each random bit its own test. -#define TEST_RANDOM_BIT(BIT) \ - TEST(PartitionAllocAddressSpaceRandomizationTest, \ - RandomBitCorrelations##BIT) { \ - RandomBitCorrelation(BIT); \ - } - -// The first 12 bits on all platforms are always 0. -TEST_RANDOM_BIT(12) -TEST_RANDOM_BIT(13) -TEST_RANDOM_BIT(14) -TEST_RANDOM_BIT(15) -TEST_RANDOM_BIT(16) -TEST_RANDOM_BIT(17) -TEST_RANDOM_BIT(18) -TEST_RANDOM_BIT(19) -TEST_RANDOM_BIT(20) -TEST_RANDOM_BIT(21) -TEST_RANDOM_BIT(22) -TEST_RANDOM_BIT(23) -TEST_RANDOM_BIT(24) -TEST_RANDOM_BIT(25) -TEST_RANDOM_BIT(26) -TEST_RANDOM_BIT(27) -TEST_RANDOM_BIT(28) -TEST_RANDOM_BIT(29) -TEST_RANDOM_BIT(30) -TEST_RANDOM_BIT(31) -#if defined(ARCH_CPU_64_BITS) -TEST_RANDOM_BIT(32) -TEST_RANDOM_BIT(33) -TEST_RANDOM_BIT(34) -TEST_RANDOM_BIT(35) -TEST_RANDOM_BIT(36) -TEST_RANDOM_BIT(37) -TEST_RANDOM_BIT(38) -TEST_RANDOM_BIT(39) -TEST_RANDOM_BIT(40) -TEST_RANDOM_BIT(41) -TEST_RANDOM_BIT(42) -TEST_RANDOM_BIT(43) -TEST_RANDOM_BIT(44) -TEST_RANDOM_BIT(45) -TEST_RANDOM_BIT(46) -TEST_RANDOM_BIT(47) -TEST_RANDOM_BIT(48) -// No platforms have more than 48 address bits. -#endif // defined(ARCH_CPU_64_BITS) - -#undef TEST_RANDOM_BIT - -// Checks that we can actually map memory in the requested range. -// TODO(crbug.com/1318466): Extend to all operating systems once they are fixed. -#if BUILDFLAG(IS_MAC) -TEST(PartitionAllocAddressSpaceRandomizationTest, CanMapInAslrRange) { - int tries = 0; - // This is overly generous, but we really don't want to make the test flaky. - constexpr int kMaxTries = 1000; - - for (tries = 0; tries < kMaxTries; tries++) { - uintptr_t requested_address = GetRandomPageBase(); - size_t size = internal::PageAllocationGranularity(); - - uintptr_t address = AllocPages( - requested_address, size, internal::PageAllocationGranularity(), - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kReadWrite), - PageTag::kPartitionAlloc); - ASSERT_NE(address, 0u); - FreePages(address, size); - - if (address == requested_address) { - break; - } - } - - EXPECT_LT(tries, kMaxTries); -} -#endif // BUILDFLAG(IS_MAC) - -} // namespace partition_alloc
diff --git a/base/allocator/partition_allocator/address_space_stats.h b/base/allocator/partition_allocator/address_space_stats.h deleted file mode 100644 index a9b4cb0..0000000 --- a/base/allocator/partition_allocator/address_space_stats.h +++ /dev/null
@@ -1,54 +0,0 @@ -// 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. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_SPACE_STATS_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_SPACE_STATS_H_ - -#include <cstddef> - -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" - -namespace partition_alloc { - -// All members are measured in super pages. -struct PoolStats { - size_t usage = 0; - - // On 32-bit, pools are mainly logical entities, intermingled with - // allocations not managed by PartitionAlloc. The "largest available - // reservation" is not possible to measure in that case. -#if BUILDFLAG(HAS_64_BIT_POINTERS) - size_t largest_available_reservation = 0; -#endif -}; - -struct AddressSpaceStats { - PoolStats regular_pool_stats; -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - PoolStats brp_pool_stats; -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) -#if BUILDFLAG(HAS_64_BIT_POINTERS) - PoolStats configurable_pool_stats; -#else -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - size_t blocklist_size; // measured in super pages - size_t blocklist_hit_count; -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) -#endif // BUILDFLAG(HAS_64_BIT_POINTERS) -#if BUILDFLAG(ENABLE_PKEYS) - PoolStats pkey_pool_stats; -#endif -}; - -// Interface passed to `AddressPoolManager::DumpStats()` to mediate -// for `AddressSpaceDumpProvider`. -class PA_COMPONENT_EXPORT(PARTITION_ALLOC) AddressSpaceStatsDumper { - public: - virtual void DumpStats(const AddressSpaceStats* address_space_stats) = 0; -}; - -} // namespace partition_alloc - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_SPACE_STATS_H_
diff --git a/base/allocator/partition_allocator/allocation_guard.cc b/base/allocator/partition_allocator/allocation_guard.cc deleted file mode 100644 index a87ee27..0000000 --- a/base/allocator/partition_allocator/allocation_guard.cc +++ /dev/null
@@ -1,42 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/allocation_guard.h" -#include "base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h" -#include "base/allocator/partition_allocator/partition_alloc_config.h" - -#if PA_CONFIG(HAS_ALLOCATION_GUARD) - -namespace partition_alloc { - -namespace { -thread_local bool g_disallow_allocations; -} // namespace - -ScopedDisallowAllocations::ScopedDisallowAllocations() { - if (g_disallow_allocations) { - PA_IMMEDIATE_CRASH(); - } - - g_disallow_allocations = true; -} - -ScopedDisallowAllocations::~ScopedDisallowAllocations() { - g_disallow_allocations = false; -} - -ScopedAllowAllocations::ScopedAllowAllocations() { - // Save the previous value, as ScopedAllowAllocations is used in all - // partitions, not just the malloc() ones(s). - saved_value_ = g_disallow_allocations; - g_disallow_allocations = false; -} - -ScopedAllowAllocations::~ScopedAllowAllocations() { - g_disallow_allocations = saved_value_; -} - -} // namespace partition_alloc - -#endif // PA_CONFIG(HAS_ALLOCATION_GUARD)
diff --git a/base/allocator/partition_allocator/allocation_guard.h b/base/allocator/partition_allocator/allocation_guard.h deleted file mode 100644 index 756c158..0000000 --- a/base/allocator/partition_allocator/allocation_guard.h +++ /dev/null
@@ -1,49 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_ALLOCATION_GUARD_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_ALLOCATION_GUARD_H_ - -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_config.h" -#include "build/build_config.h" - -namespace partition_alloc { - -#if PA_CONFIG(HAS_ALLOCATION_GUARD) - -// Disallow allocations in the scope. Does not nest. -class PA_COMPONENT_EXPORT(PARTITION_ALLOC) ScopedDisallowAllocations { - public: - ScopedDisallowAllocations(); - ~ScopedDisallowAllocations(); -}; - -// Disallow allocations in the scope. Does not nest. -class PA_COMPONENT_EXPORT(PARTITION_ALLOC) ScopedAllowAllocations { - public: - ScopedAllowAllocations(); - ~ScopedAllowAllocations(); - - private: - bool saved_value_; -}; - -#else - -struct [[maybe_unused]] ScopedDisallowAllocations {}; -struct [[maybe_unused]] ScopedAllowAllocations {}; - -#endif // PA_CONFIG(HAS_ALLOCATION_GUARD) - -} // namespace partition_alloc - -namespace base::internal { - -using ::partition_alloc::ScopedAllowAllocations; -using ::partition_alloc::ScopedDisallowAllocations; - -} // namespace base::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_ALLOCATION_GUARD_H_
diff --git a/base/allocator/partition_allocator/arm_bti_test_functions.S b/base/allocator/partition_allocator/arm_bti_test_functions.S deleted file mode 100644 index bf04e93..0000000 --- a/base/allocator/partition_allocator/arm_bti_test_functions.S +++ /dev/null
@@ -1,50 +0,0 @@ -# Copyright 2021 The Chromium Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -# This file contains a test function for checking Arm's branch target -# identification (BTI) feature, which helps mitigate jump-oriented -# programming. To get it working, BTI instructions must be executed -# on a compatible core, and the executable pages must be mapped with -# PROT_BTI. To validate that pages mapped with PROT_BTI are working -# correctly: -# 1) Allocate a read-write page. -# 2) Copy between the start and end symbols into that page. -# 3) Set the page to read-execute with PROT_BTI. -# 4) Call the first offset of the page, verify the result. -# 5) Call the second offset of the page (skipping the landing pad). -# Verify that it crashes as expected. -# This test works irrespective of whether BTI is enabled for C/C++ -# objects via -mbranch-protection=standard. - -.text -.global arm_bti_test_function -.global arm_bti_test_function_invalid_offset -.global arm_bti_test_function_end -arm_bti_test_function: - # Mark the start of this function as a valid call target. - bti jc - add x0, x0, #1 -arm_bti_test_function_invalid_offset: - # This label simulates calling an incomplete function. - # Jumping here should crash systems which support BTI. - add x0, x0, #2 - ret -arm_bti_test_function_end: - nop - -// For details see section "6.2 Program Property" in -// "ELF for the Arm 64-bit Architecture (AArch64)" -// https://github.com/ARM-software/abi-aa/blob/main/aaelf64/aaelf64.rst#62program-property -.pushsection .note.gnu.property, "a"; -.balign 8; -.long 4; -.long 0x10; -.long 0x5; -.asciz "GNU"; -.long 0xc0000000; /* GNU_PROPERTY_AARCH64_FEATURE_1_AND */ -.long 4; -.long 1; /* GNU_PROPERTY_AARCH64_BTI */; -.long 0; -.popsection -
diff --git a/base/allocator/partition_allocator/arm_bti_test_functions.h b/base/allocator/partition_allocator/arm_bti_test_functions.h deleted file mode 100644 index 485a67b..0000000 --- a/base/allocator/partition_allocator/arm_bti_test_functions.h +++ /dev/null
@@ -1,31 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_ARM_BTI_TEST_FUNCTIONS_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_ARM_BTI_TEST_FUNCTIONS_H_ - -#include "build/build_config.h" - -#if defined(ARCH_CPU_ARM64) -extern "C" { -/** - * A valid BTI function. Jumping to this funtion should not cause any problem in - * a BTI enabled environment. - **/ -int64_t arm_bti_test_function(int64_t); - -/** - * A function without proper BTI landing pad. Jumping here should crash the - * program on systems which support BTI. - **/ -int64_t arm_bti_test_function_invalid_offset(int64_t); - -/** - * A simple function which immediately returns to sender. - **/ -void arm_bti_test_function_end(void); -} -#endif // defined(ARCH_CPU_ARM64) - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_ARM_BTI_TEST_FUNCTIONS_H_
diff --git a/base/allocator/partition_allocator/build_config.md b/base/allocator/partition_allocator/build_config.md deleted file mode 100644 index 03223a5..0000000 --- a/base/allocator/partition_allocator/build_config.md +++ /dev/null
@@ -1,103 +0,0 @@ -# Build Config - -PartitionAlloc's behavior and operation can be influenced by many -different settings. Broadly, these are controlled at the top-level by -[GN args][gn-declare-args], which propagate via -[buildflags][buildflag-header] and `#defined` clauses. - -*** promo -Most of what you'll want to know exists between - -* [`//base/allocator/partition_allocator/BUILD.gn`][pa-build-gn], -* Everything else ending in `.gn` or `.gni` in - `//base/allocator/partition_allocator/`, -* [`allocator.gni`][allocator-gni], -* [`//base/allocator/BUILD.gn`][base-allocator-build-gn], and -* [`//base/BUILD.gn`][base-build-gn]. -*** - -*** aside -While Chromium promotes the `#if BUILDFLAG(FOO)` construct, some of -PartitionAlloc's behavior is governed by compound conditions `#defined` -in [`partition_alloc_config.h`][partition-alloc-config]. -*** - -*** promo -PartitionAlloc targets C++17. As the team develops standalone -PartitionAlloc, this may diverge from what the rest of Chrome browser -does, as we will be obligated to support external clients that -may not yet support newer C++ standards. - -See [Chrome-External Builds](./external_builds.md) for more. -*** - -## Select GN Args - -### `use_partition_alloc` - -Defines whether PartitionAlloc is at all available. - -Setting this `false` will entirely remove PartitionAlloc from the -Chromium build. _You probably do not want this._ - -*** note -Back when PartitionAlloc was the dedicated allocator in Blink, disabling -it was logically identical to wholly disabling it in Chromium. This GN -arg organically grew in scope with the advent of -PartitionAlloc-Everywhere and must be `true` as a prerequisite for -enabling PA-E. -*** - -### `use_partition_alloc_as_malloc` - -Does nothing special when value is `false`. Enables -[PartitionAlloc-Everywhere (PA-E)][pae-public-doc] when value is `true`. - -*** note -* While "everywhere" (in "PartitionAlloc-Everywhere") tautologically - includes Blink where PartitionAlloc originated, setting - `use_partition_alloc_as_malloc = false` does not disable PA usage in Blink, - which invokes PA explicitly (not via malloc). -* `use_partition_alloc_as_malloc = true` must not be confused - with `use_partition_alloc` (see above). -*** - -## Notable Macros - -There is an ongoing effort -[to break out PartitionAlloc into a standalone library][pa-ee-crbug]. -Once PartitionAlloc stands alone from the larger Chrome build apparatus, -the code loses access to some macros. This is not an immediate concern, -but the team needs to decide either - -* how to propagate these macros in place, or -* how to remove them, replacing them with PA-specific build config. - -A non-exhaustive list of work items: - -* `OFFICIAL_BUILD` - influences crash macros and - `PA_THREAD_CACHE_ALLOC_STATS`. These are conceptually distinct enough - to be worth separating into dedicated build controls. -* `IS_PARTITION_ALLOC_IMPL` - must be defined when PartitionAlloc is - built as a shared library. This is required to export symbols. -* `COMPONENT_BUILD` - component builds (as per - `//docs/component_build.md`) must `#define COMPONENT_BUILD`. - Additionally, to build Win32, invoker must `#define WIN32`. -* `MEMORY_TOOL_REPLACES_ALLOCATOR` -* `*_SANITIZER` - mainly influences unit tests. - -*** note -Over time, the above list should evolve into a list of macros / GN args -that influence PartitionAlloc's behavior. -*** - -[gn-declare-args]: https://gn.googlesource.com/gn/+/refs/heads/main/docs/reference.md#func_declare_args -[buildflag-header]: https://source.chromium.org/chromium/chromium/src/+/main:build/buildflag_header.gni -[pa-build-gn]: https://source.chromium.org/chromium/chromium/src/+/main:base/allocator/partition_allocator/BUILD.gn -[allocator-gni]: https://source.chromium.org/chromium/chromium/src/+/main:base/allocator/allocator.gni -[base-allocator-build-gn]: https://source.chromium.org/chromium/chromium/src/+/main:base/allocator/BUILD.gn -[base-build-gn]: https://source.chromium.org/chromium/chromium/src/+/main:base/BUILD.gn -[partition-alloc-config]: https://source.chromium.org/chromium/chromium/src/+/main:base/allocator/partition_allocator/partition_alloc_config.h -[pae-public-doc]: https://docs.google.com/document/d/1R1H9z5IVUAnXJgDjnts3nTJVcRbufWWT9ByXLgecSUM/preview -[miracleptr-doc]: https://docs.google.com/document/d/1pnnOAIz_DMWDI4oIOFoMAqLnf_MZ2GsrJNb_dbQ3ZBg/preview -[pa-ee-crbug]: https://crbug.com/1151236
diff --git a/base/allocator/partition_allocator/build_overrides/build.gni b/base/allocator/partition_allocator/build_overrides/build.gni deleted file mode 100644 index 30c0b64..0000000 --- a/base/allocator/partition_allocator/build_overrides/build.gni +++ /dev/null
@@ -1,9 +0,0 @@ -# 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. - -# This file will be used to check out PartitionAlloc and to build it as -# standalone library. In this case, PartitionAlloc needs to define -# build_with_chromium. If building PartitionAlloc as a part of chromium, -# chromium will provide build_with_chromium=true. -build_with_chromium = false
diff --git a/base/allocator/partition_allocator/build_overrides/partition_alloc.gni b/base/allocator/partition_allocator/build_overrides/partition_alloc.gni deleted file mode 100644 index 19fd2de..0000000 --- a/base/allocator/partition_allocator/build_overrides/partition_alloc.gni +++ /dev/null
@@ -1,18 +0,0 @@ -# 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_overrides/build.gni") - -# This is the default build configuration when building PartitionAlloc -# as a standalone library. -# If embedders want to use PartitionAlloc, they need to create their own -# //build_overrides/partition_alloc.gni and define their own PartitionAlloc -# configuration. - -use_partition_alloc_as_malloc_default = false -use_allocator_shim_default = false -enable_backup_ref_ptr_support_default = false -put_ref_count_in_previous_slot_default = true -enable_backup_ref_ptr_slow_checks_default = false -enable_dangling_raw_ptr_checks_default = false
diff --git a/base/allocator/partition_allocator/compressed_pointer.cc b/base/allocator/partition_allocator/compressed_pointer.cc deleted file mode 100644 index d91ac34..0000000 --- a/base/allocator/partition_allocator/compressed_pointer.cc +++ /dev/null
@@ -1,28 +0,0 @@ -// 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. - -#include "base/allocator/partition_allocator/compressed_pointer.h" - -#if PA_CONFIG(POINTER_COMPRESSION) - -namespace partition_alloc::internal { - -// We keep the useful part in |g_base_| as 1s to speed up decompression. -alignas(kPartitionCachelineSize) - PA_COMPONENT_EXPORT(PARTITION_ALLOC) CompressedPointerBaseGlobal::Base - CompressedPointerBaseGlobal::g_base_ = {.base = kUsefulBitsMask}; - -void CompressedPointerBaseGlobal::SetBase(uintptr_t base) { - PA_DCHECK(!IsSet()); - PA_DCHECK((base & kUsefulBitsMask) == 0); - g_base_.base = base | kUsefulBitsMask; -} - -void CompressedPointerBaseGlobal::ResetBaseForTesting() { - g_base_.base = kUsefulBitsMask; -} - -} // namespace partition_alloc::internal - -#endif // PA_CONFIG(POINTER_COMPRESSION)
diff --git a/base/allocator/partition_allocator/compressed_pointer.h b/base/allocator/partition_allocator/compressed_pointer.h deleted file mode 100644 index 7432fb5..0000000 --- a/base/allocator/partition_allocator/compressed_pointer.h +++ /dev/null
@@ -1,667 +0,0 @@ -// 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. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_COMPRESSED_POINTER_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_COMPRESSED_POINTER_H_ - -#include <climits> -#include <type_traits> - -#include "base/allocator/partition_allocator/partition_address_space.h" -#include "base/allocator/partition_allocator/partition_alloc_base/bits.h" -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" - -#if PA_CONFIG(POINTER_COMPRESSION) - -#if !PA_CONFIG(GLUE_CORE_POOLS) -#error "Pointer compression only works with glued pools" -#endif -#if PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE) -#error "Pointer compression currently supports constant pool size" -#endif - -#endif // PA_CONFIG(POINTER_COMPRESSION) - -namespace partition_alloc { - -namespace internal { - -template <typename T1, typename T2> -constexpr bool IsDecayedSame = - std::is_same_v<std::decay_t<T1>, std::decay_t<T2>>; - -#if PA_CONFIG(POINTER_COMPRESSION) - -// Pointer compression works by storing only the 'useful' 32-bit part of the -// pointer. The other half (the base) is stored in a global variable -// (CompressedPointerBaseGlobal::g_base_), which is used on decompression. To -// support fast branchless decompression of nullptr, we use the most significant -// bit in the compressed pointer to leverage sign-extension (for non-nullptr -// pointers, the most significant bit is set, whereas for nullptr it's not). -// Using this bit and supporting heaps larger than 4GB relies on having -// alignment bits in pointers. Assuming that all pointers point to at least -// 8-byte alignment objects, pointer compression can support heaps of size <= -// 16GB. -// ((3 alignment bits) = (1 bit for sign-extension) + (2 bits for 16GB heap)). -// -// Example: heap base: 0x4b0'ffffffff -// - g_base: 0x4b3'ffffffff (lower 34 bits set) -// - normal pointer: 0x4b2'a08b6480 -// - compression: -// - shift right by 3: 0x96'54116c90 -// - truncate: 0x54116c90 -// - mark MSB: 0xd4116c90 -// - decompression: -// - sign-extend: 0xffffffff'd4116c90 -// - shift left by 3: 0xfffffffe'a08b6480 -// - 'and' with g_base: 0x000004b2'a08b6480 -// -// - nullptr: 0x00000000'00000000 -// - compression: -// - shift right by 3: 0x00000000'00000000 -// - truncate: 0x00000000 -// - (don't mark MSB for nullptr) -// - decompression: -// - sign-extend: 0x00000000'00000000 -// - shift left by 3: 0x00000000'00000000 -// - 'and' with g_base: 0x00000000'00000000 -// -// Pointer compression relies on having both the regular and the BRP pool (core -// pools) 'glued', so that the same base could be used for both. For simplicity, -// the configurations with dynamically selected pool size are not supported. -// However, they can be at the cost of performing an extra load for -// core-pools-shift-size on both compression and decompression. - -class CompressedPointerBaseGlobal final { - public: - static constexpr size_t kUsefulBits = - base::bits::CountTrailingZeroBits(PartitionAddressSpace::CorePoolsSize()); - static_assert(kUsefulBits >= sizeof(uint32_t) * CHAR_BIT); - static constexpr size_t kBitsToShift = - kUsefulBits - sizeof(uint32_t) * CHAR_BIT; - - CompressedPointerBaseGlobal() = delete; - - // Attribute const allows the compiler to assume that - // CompressedPointerBaseGlobal::g_base_ doesn't change (e.g. across calls) and - // thereby avoid redundant loads. - PA_ALWAYS_INLINE __attribute__((const)) static uintptr_t Get() { - PA_DCHECK(IsBaseConsistent()); - return g_base_.base; - } - - PA_ALWAYS_INLINE static bool IsSet() { - PA_DCHECK(IsBaseConsistent()); - return (g_base_.base & ~kUsefulBitsMask) != 0; - } - - private: - static constexpr uintptr_t kUsefulBitsMask = - PartitionAddressSpace::CorePoolsSize() - 1; - - static union alignas(kPartitionCachelineSize) - PA_COMPONENT_EXPORT(PARTITION_ALLOC) Base { - uintptr_t base; - char cache_line[kPartitionCachelineSize]; - } g_base_ PA_CONSTINIT; - - PA_ALWAYS_INLINE static bool IsBaseConsistent() { - return kUsefulBitsMask == (g_base_.base & kUsefulBitsMask); - } - - static void SetBase(uintptr_t base); - static void ResetBaseForTesting(); - - friend class PartitionAddressSpace; -}; - -#endif // PA_CONFIG(POINTER_COMPRESSION) - -} // namespace internal - -#if PA_CONFIG(POINTER_COMPRESSION) - -template <typename T> -class PA_TRIVIAL_ABI CompressedPointer final { - public: - using UnderlyingType = uint32_t; - - PA_ALWAYS_INLINE constexpr CompressedPointer() = default; - PA_ALWAYS_INLINE explicit CompressedPointer(T* ptr) : value_(Compress(ptr)) {} - PA_ALWAYS_INLINE constexpr explicit CompressedPointer(std::nullptr_t) - : value_(0u) {} - - PA_ALWAYS_INLINE constexpr CompressedPointer(const CompressedPointer&) = - default; - PA_ALWAYS_INLINE constexpr CompressedPointer( - CompressedPointer&& other) noexcept = default; - - template <typename U, - std::enable_if_t<std::is_convertible_v<U*, T*>>* = nullptr> - PA_ALWAYS_INLINE constexpr CompressedPointer( - const CompressedPointer<U>& other) { - if constexpr (internal::IsDecayedSame<T, U>) { - // When pointers have the same type modulo constness, avoid the - // compress-decompress round. - value_ = other.value_; - } else { - // When the types are different, perform the round, because the pointer - // may need to be adjusted. - // TODO(1376980): Avoid the cycle here. - value_ = Compress(other.get()); - } - } - - template <typename U, - std::enable_if_t<std::is_convertible_v<U*, T*>>* = nullptr> - PA_ALWAYS_INLINE constexpr CompressedPointer( - CompressedPointer<U>&& other) noexcept - : CompressedPointer(other) {} - - ~CompressedPointer() = default; - - PA_ALWAYS_INLINE constexpr CompressedPointer& operator=( - const CompressedPointer&) = default; - PA_ALWAYS_INLINE constexpr CompressedPointer& operator=( - CompressedPointer&& other) noexcept = default; - - template <typename U, - std::enable_if_t<std::is_convertible_v<U*, T*>>* = nullptr> - PA_ALWAYS_INLINE constexpr CompressedPointer& operator=( - const CompressedPointer<U>& other) { - CompressedPointer copy(other); - value_ = copy.value_; - return *this; - } - - template <typename U, - std::enable_if_t<std::is_convertible_v<U*, T*>>* = nullptr> - PA_ALWAYS_INLINE constexpr CompressedPointer& operator=( - CompressedPointer<U>&& other) noexcept { - *this = other; - return *this; - } - - // Don't perform compression when assigning to nullptr. - PA_ALWAYS_INLINE constexpr CompressedPointer& operator=(std::nullptr_t) { - value_ = 0u; - return *this; - } - - PA_ALWAYS_INLINE T* get() const { return Decompress(value_); } - - PA_ALWAYS_INLINE constexpr bool is_nonnull() const { return value_; } - - PA_ALWAYS_INLINE constexpr UnderlyingType GetAsIntegral() const { - return value_; - } - - PA_ALWAYS_INLINE constexpr explicit operator bool() const { - return is_nonnull(); - } - - template <typename U = T, - std::enable_if_t<!std::is_void_v<std::remove_cv_t<U>>>* = nullptr> - PA_ALWAYS_INLINE U& operator*() const { - PA_DCHECK(is_nonnull()); - return *get(); - } - - PA_ALWAYS_INLINE T* operator->() const { - PA_DCHECK(is_nonnull()); - return get(); - } - - PA_ALWAYS_INLINE constexpr void swap(CompressedPointer& other) { - std::swap(value_, other.value_); - } - - private: - template <typename> - friend class CompressedPointer; - - static constexpr size_t kBitsForSignExtension = 1; - static constexpr size_t kOverallBitsToShift = - internal::CompressedPointerBaseGlobal::kBitsToShift + - kBitsForSignExtension; - - PA_ALWAYS_INLINE static UnderlyingType Compress(T* ptr) { - static constexpr size_t kMinimalRequiredAlignment = 8; - static_assert((1 << kOverallBitsToShift) == kMinimalRequiredAlignment); - -#if BUILDFLAG(PA_DCHECK_IS_ON) - PA_DCHECK(reinterpret_cast<uintptr_t>(ptr) % kMinimalRequiredAlignment == - 0); - PA_DCHECK(internal::CompressedPointerBaseGlobal::IsSet()); - - const uintptr_t base = internal::CompressedPointerBaseGlobal::Get(); - static constexpr size_t kCorePoolsBaseMask = - ~(internal::PartitionAddressSpace::CorePoolsSize() - 1); - PA_DCHECK(!ptr || - (base & kCorePoolsBaseMask) == - (reinterpret_cast<uintptr_t>(ptr) & kCorePoolsBaseMask)); -#endif // BUILDFLAG(PA_DCHECK_IS_ON) - - const auto uptr = reinterpret_cast<uintptr_t>(ptr); - // Shift the pointer and truncate. - auto compressed = static_cast<UnderlyingType>(uptr >> kOverallBitsToShift); - // If the pointer is non-null, mark the most-significant-bit to sign-extend - // it on decompression. Assuming compression is a significantly less - // frequent operation, we let more work here in favor of faster - // decompression. - // TODO(1376980): Avoid this by overreserving the heap. - if (compressed) { - compressed |= (1u << (sizeof(uint32_t) * CHAR_BIT - 1)); - } - - return compressed; - } - - PA_ALWAYS_INLINE static T* Decompress(UnderlyingType ptr) { - PA_DCHECK(internal::CompressedPointerBaseGlobal::IsSet()); - const uintptr_t base = internal::CompressedPointerBaseGlobal::Get(); - // Treat compressed pointer as signed and cast it to uint64_t, which will - // sign-extend it. Then, shift the result by one. It's important to shift - // the already unsigned value, as otherwise it would result in undefined - // behavior. - const uint64_t mask = static_cast<uint64_t>(static_cast<int32_t>(ptr)) - << (kOverallBitsToShift); - return reinterpret_cast<T*>(mask & base); - } - - UnderlyingType value_; -}; - -template <typename T> -PA_ALWAYS_INLINE constexpr void swap(CompressedPointer<T>& a, - CompressedPointer<T>& b) { - a.swap(b); -} - -// operators==. -template <typename T, typename U> -PA_ALWAYS_INLINE bool operator==(CompressedPointer<T> a, - CompressedPointer<U> b) { - if constexpr (internal::IsDecayedSame<T, U>) { - // When pointers have the same type modulo constness, simply compare - // compressed values. - return a.GetAsIntegral() == b.GetAsIntegral(); - } else { - // When the types are different, compare decompressed pointers, because the - // pointers may need to be adjusted. - // TODO(1376980): Avoid decompression here. - return a.get() == b.get(); - } -} - -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator==(CompressedPointer<T> a, U* b) { - // Do compression, since it is less expensive. - return a == static_cast<CompressedPointer<U>>(b); -} - -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator==(T* a, CompressedPointer<U> b) { - return b == a; -} - -template <typename T> -PA_ALWAYS_INLINE constexpr bool operator==(CompressedPointer<T> a, - std::nullptr_t) { - return !a.is_nonnull(); -} - -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator==(std::nullptr_t, - CompressedPointer<U> b) { - return b == nullptr; -} - -// operators!=. -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator!=(CompressedPointer<T> a, - CompressedPointer<U> b) { - return !(a == b); -} - -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator!=(CompressedPointer<T> a, U* b) { - // Do compression, since it is less expensive. - return a != static_cast<CompressedPointer<U>>(b); -} - -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator!=(T* a, CompressedPointer<U> b) { - return b != a; -} - -template <typename T> -PA_ALWAYS_INLINE constexpr bool operator!=(CompressedPointer<T> a, - std::nullptr_t) { - return a.is_nonnull(); -} - -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator!=(std::nullptr_t, - CompressedPointer<U> b) { - return b != nullptr; -} - -// operators<. -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator<(CompressedPointer<T> a, - CompressedPointer<U> b) { - if constexpr (internal::IsDecayedSame<T, U>) { - // When pointers have the same type modulo constness, simply compare - // compressed values. - return a.GetAsIntegral() < b.GetAsIntegral(); - } else { - // When the types are different, compare decompressed pointers, because the - // pointers may need to be adjusted. - // TODO(1376980): Avoid decompression here. - return a.get() < b.get(); - } -} - -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator<(CompressedPointer<T> a, U* b) { - // Do compression, since it is less expensive. - return a < static_cast<CompressedPointer<U>>(b); -} - -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator<(T* a, CompressedPointer<U> b) { - // Do compression, since it is less expensive. - return static_cast<CompressedPointer<T>>(a) < b; -} - -// operators<=. -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator<=(CompressedPointer<T> a, - CompressedPointer<U> b) { - if constexpr (internal::IsDecayedSame<T, U>) { - // When pointers have the same type modulo constness, simply compare - // compressed values. - return a.GetAsIntegral() <= b.GetAsIntegral(); - } else { - // When the types are different, compare decompressed pointers, because the - // pointers may need to be adjusted. - // TODO(1376980): Avoid decompression here. - return a.get() <= b.get(); - } -} - -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator<=(CompressedPointer<T> a, U* b) { - // Do compression, since it is less expensive. - return a <= static_cast<CompressedPointer<U>>(b); -} - -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator<=(T* a, CompressedPointer<U> b) { - // Do compression, since it is less expensive. - return static_cast<CompressedPointer<T>>(a) <= b; -} - -// operators>. -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator>(CompressedPointer<T> a, - CompressedPointer<U> b) { - return !(a <= b); -} - -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator>(CompressedPointer<T> a, U* b) { - // Do compression, since it is less expensive. - return a > static_cast<CompressedPointer<U>>(b); -} - -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator>(T* a, CompressedPointer<U> b) { - // Do compression, since it is less expensive. - return static_cast<CompressedPointer<T>>(a) > b; -} - -// operators>=. -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator>=(CompressedPointer<T> a, - CompressedPointer<U> b) { - return !(a < b); -} - -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator>=(CompressedPointer<T> a, U* b) { - // Do compression, since it is less expensive. - return a >= static_cast<CompressedPointer<U>>(b); -} - -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator>=(T* a, CompressedPointer<U> b) { - // Do compression, since it is less expensive. - return static_cast<CompressedPointer<T>>(a) >= b; -} - -#endif // PA_CONFIG(POINTER_COMPRESSION) - -// Simple wrapper over the raw pointer. -template <typename T> -class PA_TRIVIAL_ABI UncompressedPointer final { - public: - PA_ALWAYS_INLINE constexpr UncompressedPointer() = default; - PA_ALWAYS_INLINE constexpr explicit UncompressedPointer(T* ptr) : ptr_(ptr) {} - PA_ALWAYS_INLINE constexpr explicit UncompressedPointer(std::nullptr_t) - : ptr_(nullptr) {} - - PA_ALWAYS_INLINE constexpr UncompressedPointer(const UncompressedPointer&) = - default; - PA_ALWAYS_INLINE constexpr UncompressedPointer( - UncompressedPointer&& other) noexcept = default; - - template <typename U, - std::enable_if_t<std::is_convertible_v<U*, T*>>* = nullptr> - PA_ALWAYS_INLINE constexpr explicit UncompressedPointer( - const UncompressedPointer<U>& other) - : ptr_(other.ptr_) {} - - template <typename U, - std::enable_if_t<std::is_convertible_v<U*, T*>>* = nullptr> - PA_ALWAYS_INLINE constexpr explicit UncompressedPointer( - UncompressedPointer<U>&& other) noexcept - : ptr_(std::move(other.ptr_)) {} - - ~UncompressedPointer() = default; - - PA_ALWAYS_INLINE constexpr UncompressedPointer& operator=( - const UncompressedPointer&) = default; - PA_ALWAYS_INLINE constexpr UncompressedPointer& operator=( - UncompressedPointer&& other) noexcept = default; - - template <typename U, - std::enable_if_t<std::is_convertible_v<U*, T*>>* = nullptr> - PA_ALWAYS_INLINE constexpr UncompressedPointer& operator=( - const UncompressedPointer<U>& other) { - ptr_ = other.ptr_; - return *this; - } - - template <typename U, - std::enable_if_t<std::is_convertible_v<U*, T*>>* = nullptr> - PA_ALWAYS_INLINE constexpr UncompressedPointer& operator=( - UncompressedPointer<U>&& other) noexcept { - ptr_ = std::move(other.ptr_); - return *this; - } - - PA_ALWAYS_INLINE constexpr UncompressedPointer& operator=(std::nullptr_t) { - ptr_ = nullptr; - return *this; - } - - PA_ALWAYS_INLINE constexpr T* get() const { return ptr_; } - - PA_ALWAYS_INLINE constexpr bool is_nonnull() const { return ptr_; } - - PA_ALWAYS_INLINE constexpr explicit operator bool() const { - return is_nonnull(); - } - - template <typename U = T, - std::enable_if_t<!std::is_void_v<std::remove_cv_t<U>>>* = nullptr> - PA_ALWAYS_INLINE constexpr U& operator*() const { - PA_DCHECK(is_nonnull()); - return *get(); - } - - PA_ALWAYS_INLINE constexpr T* operator->() const { - PA_DCHECK(is_nonnull()); - return get(); - } - - PA_ALWAYS_INLINE constexpr void swap(UncompressedPointer& other) { - std::swap(ptr_, other.ptr_); - } - - private: - template <typename> - friend class UncompressedPointer; - - T* ptr_; -}; - -template <typename T> -PA_ALWAYS_INLINE constexpr void swap(UncompressedPointer<T>& a, - UncompressedPointer<T>& b) { - a.swap(b); -} - -// operators==. -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator==(UncompressedPointer<T> a, - UncompressedPointer<U> b) { - return a.get() == b.get(); -} - -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator==(UncompressedPointer<T> a, U* b) { - return a == static_cast<UncompressedPointer<U>>(b); -} - -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator==(T* a, UncompressedPointer<U> b) { - return b == a; -} - -template <typename T> -PA_ALWAYS_INLINE constexpr bool operator==(UncompressedPointer<T> a, - std::nullptr_t) { - return !a.is_nonnull(); -} - -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator==(std::nullptr_t, - UncompressedPointer<U> b) { - return b == nullptr; -} - -// operators!=. -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator!=(UncompressedPointer<T> a, - UncompressedPointer<U> b) { - return !(a == b); -} - -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator!=(UncompressedPointer<T> a, U* b) { - return a != static_cast<UncompressedPointer<U>>(b); -} - -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator!=(T* a, UncompressedPointer<U> b) { - return b != a; -} - -template <typename T> -PA_ALWAYS_INLINE constexpr bool operator!=(UncompressedPointer<T> a, - std::nullptr_t) { - return a.is_nonnull(); -} - -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator!=(std::nullptr_t, - UncompressedPointer<U> b) { - return b != nullptr; -} - -// operators<. -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator<(UncompressedPointer<T> a, - UncompressedPointer<U> b) { - return a.get() < b.get(); -} - -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator<(UncompressedPointer<T> a, U* b) { - return a < static_cast<UncompressedPointer<U>>(b); -} - -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator<(T* a, UncompressedPointer<U> b) { - return static_cast<UncompressedPointer<T>>(a) < b; -} - -// operators<=. -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator<=(UncompressedPointer<T> a, - UncompressedPointer<U> b) { - return a.get() <= b.get(); -} - -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator<=(UncompressedPointer<T> a, U* b) { - return a <= static_cast<UncompressedPointer<U>>(b); -} - -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator<=(T* a, UncompressedPointer<U> b) { - return static_cast<UncompressedPointer<T>>(a) <= b; -} - -// operators>. -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator>(UncompressedPointer<T> a, - UncompressedPointer<U> b) { - return !(a <= b); -} - -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator>(UncompressedPointer<T> a, U* b) { - return a > static_cast<UncompressedPointer<U>>(b); -} - -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator>(T* a, UncompressedPointer<U> b) { - return static_cast<UncompressedPointer<T>>(a) > b; -} - -// operators>=. -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator>=(UncompressedPointer<T> a, - UncompressedPointer<U> b) { - return !(a < b); -} - -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator>=(UncompressedPointer<T> a, U* b) { - return a >= static_cast<UncompressedPointer<U>>(b); -} - -template <typename T, typename U> -PA_ALWAYS_INLINE constexpr bool operator>=(T* a, UncompressedPointer<U> b) { - return static_cast<UncompressedPointer<T>>(a) >= b; -} - -} // namespace partition_alloc - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_COMPRESSED_POINTER_H_
diff --git a/base/allocator/partition_allocator/compressed_pointer_unittest.cc b/base/allocator/partition_allocator/compressed_pointer_unittest.cc deleted file mode 100644 index 9ec1c10..0000000 --- a/base/allocator/partition_allocator/compressed_pointer_unittest.cc +++ /dev/null
@@ -1,439 +0,0 @@ -// 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. - -#include "base/allocator/partition_allocator/compressed_pointer.h" - -#include "base/allocator/partition_allocator/partition_alloc.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace partition_alloc { - -namespace { - -struct Base { - double a; -}; -struct Derived : Base { - double b; -}; -struct Mixin { - double c; -}; -struct DerivedWithMixin : Base, Mixin { - double d; -}; - -using PAAllocator = internal::PartitionAllocator<internal::ThreadSafe>; - -struct PADeleter final { - void operator()(void* ptr) const { allocator_.root()->Free(ptr); } - PAAllocator& allocator_; -}; - -template <typename T, typename... Args> -std::unique_ptr<T, PADeleter> make_pa_unique(PAAllocator& alloc, - Args&&... args) { - T* result = new (alloc.root()->Alloc(sizeof(T), nullptr)) - T(std::forward<Args>(args)...); - return std::unique_ptr<T, PADeleter>(result, PADeleter{alloc}); -} - -template <typename T> -std::unique_ptr<T[], PADeleter> make_pa_array_unique(PAAllocator& alloc, - size_t num) { - T* result = new (alloc.root()->Alloc(sizeof(T) * num, nullptr)) T(); - return std::unique_ptr<T[], PADeleter>(result, PADeleter{alloc}); -} - -// Test that pointer types are trivial. -#if PA_CONFIG(POINTER_COMPRESSION) -static_assert( - std::is_trivially_default_constructible_v<CompressedPointer<Base>>); -static_assert(std::is_trivially_copy_constructible_v<CompressedPointer<Base>>); -static_assert(std::is_trivially_move_constructible_v<CompressedPointer<Base>>); -static_assert(std::is_trivially_copy_assignable_v<CompressedPointer<Base>>); -static_assert(std::is_trivially_move_assignable_v<CompressedPointer<Base>>); -#endif // PA_CONFIG(POINTER_COMPRESSION) -static_assert( - std::is_trivially_default_constructible_v<UncompressedPointer<Base>>); -static_assert( - std::is_trivially_copy_constructible_v<UncompressedPointer<Base>>); -static_assert( - std::is_trivially_move_constructible_v<UncompressedPointer<Base>>); -static_assert(std::is_trivially_copy_assignable_v<UncompressedPointer<Base>>); -static_assert(std::is_trivially_move_assignable_v<UncompressedPointer<Base>>); - -} // namespace - -struct UncompressedTypeTag {}; -struct CompressedTypeTag {}; - -template <typename TagType> -class CompressedPointerTest : public ::testing::Test { - public: -#if PA_CONFIG(POINTER_COMPRESSION) - template <typename T> - using PointerType = - std::conditional_t<std::is_same_v<TagType, CompressedTypeTag>, - CompressedPointer<T>, - UncompressedPointer<T>>; -#else // PA_CONFIG(POINTER_COMPRESSION) - template <typename T> - using PointerType = UncompressedPointer<T>; -#endif // PA_CONFIG(POINTER_COMPRESSION) - - CompressedPointerTest() { - allocator_.init({PartitionOptions::AlignedAlloc::kDisallowed, - PartitionOptions::ThreadCache::kDisabled, - PartitionOptions::Quarantine::kDisallowed, - PartitionOptions::Cookie::kDisallowed, - PartitionOptions::BackupRefPtr::kDisabled, - PartitionOptions::BackupRefPtrZapping::kDisabled, - PartitionOptions::UseConfigurablePool::kNo}); - } - - protected: - internal::PartitionAllocator<internal::ThreadSafe> allocator_; -}; - -#if PA_CONFIG(POINTER_COMPRESSION) -using ObjectTypes = ::testing::Types<UncompressedTypeTag, CompressedTypeTag>; -#else -using ObjectTypes = ::testing::Types<UncompressedTypeTag>; -#endif - -TYPED_TEST_SUITE(CompressedPointerTest, ObjectTypes); - -TYPED_TEST(CompressedPointerTest, NullConstruction) { - using DoublePointer = typename TestFixture::template PointerType<double>; - { - DoublePointer p = static_cast<DoublePointer>(nullptr); - EXPECT_FALSE(p.is_nonnull()); - EXPECT_FALSE(p.get()); - EXPECT_EQ(p, nullptr); - } - { - DoublePointer p1 = static_cast<DoublePointer>(nullptr); - DoublePointer p2 = p1; - EXPECT_FALSE(p2.is_nonnull()); - EXPECT_FALSE(p2.get()); - EXPECT_EQ(p2, nullptr); - } - { - DoublePointer p1 = static_cast<DoublePointer>(nullptr); - DoublePointer p2 = std::move(p1); - EXPECT_FALSE(p2.is_nonnull()); - EXPECT_FALSE(p2.get()); - EXPECT_EQ(p2, nullptr); - } -} - -TYPED_TEST(CompressedPointerTest, NullAssignment) { - using DoublePointer = typename TestFixture::template PointerType<double>; - { - DoublePointer p; - p = static_cast<DoublePointer>(nullptr); - EXPECT_FALSE(p.is_nonnull()); - EXPECT_FALSE(p.get()); - EXPECT_EQ(p.get(), nullptr); - EXPECT_EQ(p, nullptr); - } - { - DoublePointer p1 = DoublePointer(nullptr), p2; - p2 = p1; - EXPECT_FALSE(p2.is_nonnull()); - EXPECT_FALSE(p2.get()); - EXPECT_EQ(p2.get(), nullptr); - EXPECT_EQ(p2, nullptr); - } - { - DoublePointer p1 = DoublePointer(nullptr), p2; - p2 = std::move(p1); - EXPECT_FALSE(p2.is_nonnull()); - EXPECT_FALSE(p2.get()); - EXPECT_EQ(p2.get(), nullptr); - EXPECT_EQ(p2, nullptr); - } -} - -TYPED_TEST(CompressedPointerTest, SameTypeValueConstruction) { - using DoublePointer = typename TestFixture::template PointerType<double>; - auto d = make_pa_unique<double>(this->allocator_); - { - DoublePointer p = static_cast<DoublePointer>(d.get()); - EXPECT_TRUE(p.is_nonnull()); - EXPECT_EQ(p.get(), d.get()); - EXPECT_EQ(p, d.get()); - } - { - DoublePointer p1 = static_cast<DoublePointer>(d.get()); - DoublePointer p2 = p1; - EXPECT_TRUE(p2.is_nonnull()); - EXPECT_EQ(p2.get(), d.get()); - EXPECT_EQ(p2, p1); - EXPECT_EQ(p2, d.get()); - } - { - DoublePointer p1 = static_cast<DoublePointer>(d.get()); - DoublePointer p2 = std::move(p1); - EXPECT_TRUE(p2.is_nonnull()); - EXPECT_EQ(p2.get(), d.get()); - EXPECT_EQ(p2, d.get()); - } -} - -TYPED_TEST(CompressedPointerTest, SameTypeValueAssignment) { - using DoublePointer = typename TestFixture::template PointerType<double>; - auto d = make_pa_unique<double>(this->allocator_); - { - DoublePointer p; - p = static_cast<DoublePointer>(d.get()); - EXPECT_TRUE(p.is_nonnull()); - EXPECT_EQ(p.get(), d.get()); - EXPECT_EQ(p, d.get()); - } - { - DoublePointer p1 = static_cast<DoublePointer>(d.get()); - DoublePointer p2; - p2 = p1; - EXPECT_TRUE(p2.is_nonnull()); - EXPECT_EQ(p2.get(), d.get()); - EXPECT_EQ(p2, p1); - EXPECT_EQ(p2, d.get()); - } - { - DoublePointer p1 = static_cast<DoublePointer>(d.get()); - DoublePointer p2; - p2 = std::move(p1); - EXPECT_TRUE(p2.is_nonnull()); - EXPECT_EQ(p2.get(), d.get()); - EXPECT_EQ(p2, d.get()); - } -} - -TYPED_TEST(CompressedPointerTest, - HeterogeneousValueConstructionSamePointerValue) { - using BasePointer = typename TestFixture::template PointerType<Base>; - auto d = make_pa_unique<Derived>(this->allocator_); - { - BasePointer p = static_cast<BasePointer>(d.get()); - EXPECT_TRUE(p.is_nonnull()); - EXPECT_EQ(p.get(), d.get()); - } - { - BasePointer p1 = static_cast<BasePointer>(d.get()); - BasePointer p2 = p1; - EXPECT_TRUE(p2.is_nonnull()); - EXPECT_EQ(p2.get(), d.get()); - EXPECT_EQ(p2, p1); - EXPECT_EQ(p2, d.get()); - } - { - BasePointer p1 = static_cast<BasePointer>(d.get()); - BasePointer p2 = std::move(p1); - EXPECT_TRUE(p2.is_nonnull()); - EXPECT_EQ(p2.get(), d.get()); - EXPECT_EQ(p2, d.get()); - } -} - -TYPED_TEST(CompressedPointerTest, - HeterogeneousValueAssignmentSamePointerValue) { - using BasePointer = typename TestFixture::template PointerType<Base>; - auto d = make_pa_unique<Derived>(this->allocator_); - { - BasePointer p; - p = static_cast<BasePointer>(d.get()); - EXPECT_TRUE(p.is_nonnull()); - EXPECT_EQ(p.get(), d.get()); - } - { - BasePointer p1 = static_cast<BasePointer>(d.get()); - BasePointer p2; - p2 = p1; - EXPECT_TRUE(p2.is_nonnull()); - EXPECT_EQ(p2.get(), d.get()); - EXPECT_EQ(p2, p1); - EXPECT_EQ(p2, d.get()); - } - { - BasePointer p1 = static_cast<BasePointer>(d.get()); - BasePointer p2; - p2 = std::move(p1); - EXPECT_TRUE(p2.is_nonnull()); - EXPECT_EQ(p2.get(), d.get()); - EXPECT_EQ(p2, d.get()); - } -} - -TYPED_TEST(CompressedPointerTest, - HeterogeneousValueConstructionDifferentPointerValues) { - using MixinPointer = typename TestFixture::template PointerType<Mixin>; - auto d = make_pa_unique<DerivedWithMixin>(this->allocator_); - { - MixinPointer p = static_cast<MixinPointer>(d.get()); - ASSERT_NE(static_cast<void*>(p.get()), static_cast<void*>(d.get())); - } - { - MixinPointer p = static_cast<MixinPointer>(d.get()); - EXPECT_TRUE(p.is_nonnull()); - EXPECT_EQ(p.get(), d.get()); - } - { - MixinPointer p1 = static_cast<MixinPointer>(d.get()); - MixinPointer p2 = p1; - EXPECT_TRUE(p2.is_nonnull()); - EXPECT_EQ(p2.get(), d.get()); - EXPECT_EQ(p2, p1); - EXPECT_EQ(p2, d.get()); - } - { - MixinPointer p1 = static_cast<MixinPointer>(d.get()); - MixinPointer p2 = std::move(p1); - EXPECT_TRUE(p2.is_nonnull()); - EXPECT_EQ(p2.get(), d.get()); - EXPECT_EQ(p2, d.get()); - } -} - -TYPED_TEST(CompressedPointerTest, - HeterogeneousValueAssignmentDifferentPointerValue) { - using MixinPointer = typename TestFixture::template PointerType<Mixin>; - auto d = make_pa_unique<DerivedWithMixin>(this->allocator_); - { - MixinPointer p; - p = static_cast<MixinPointer>(d.get()); - ASSERT_NE(static_cast<void*>(p.get()), static_cast<void*>(d.get())); - } - { - MixinPointer p; - p = static_cast<MixinPointer>(d.get()); - EXPECT_TRUE(p.is_nonnull()); - EXPECT_EQ(p.get(), d.get()); - } - { - MixinPointer p1 = static_cast<MixinPointer>(d.get()); - MixinPointer p2; - p2 = p1; - EXPECT_TRUE(p2.is_nonnull()); - EXPECT_EQ(p2.get(), d.get()); - EXPECT_EQ(p2, p1); - EXPECT_EQ(p2, d.get()); - } - { - MixinPointer p1 = static_cast<MixinPointer>(d.get()); - MixinPointer p2; - p2 = std::move(p1); - EXPECT_TRUE(p2.is_nonnull()); - EXPECT_EQ(p2.get(), d.get()); - EXPECT_EQ(p2, d.get()); - } -} - -namespace { - -template <template <typename> class PointerType, - typename T1, - typename T2, - typename U> -void EqualityTest(U* raw) { - PointerType<T1> p1 = static_cast<PointerType<T1>>(raw); - PointerType<T2> p2 = static_cast<PointerType<T2>>(raw); - EXPECT_EQ(p1, raw); - EXPECT_EQ(p2, raw); - EXPECT_EQ(raw, p1); - EXPECT_EQ(raw, p2); - EXPECT_EQ(p1, p2); -} - -template <template <typename> class PointerType, - typename T1, - typename T2, - typename U> -void CompareTest(U* array) { - PointerType<T1> p0 = static_cast<PointerType<T1>>(&array[0]); - PointerType<T2> p1 = static_cast<PointerType<T2>>(&array[1]); - { - EXPECT_NE(p0, &array[1]); - EXPECT_NE(p0, p1); - EXPECT_NE(p1, &array[0]); - EXPECT_NE(p1, p0); - } - { - EXPECT_LT(p0, &array[1]); - EXPECT_LT(&array[0], p1); - EXPECT_LT(p0, p1); - } - { - EXPECT_LE(p0, &array[0]); - EXPECT_LE(p0, &array[1]); - EXPECT_LE(&array[0], p0); - - EXPECT_LE(&array[1], p1); - EXPECT_LE(p1, &array[1]); - - auto p2 = p0; - EXPECT_LE(p0, p2); - EXPECT_LE(p2, p1); - } - { - EXPECT_GT(&array[1], p0); - EXPECT_GT(p1, &array[0]); - EXPECT_GT(p1, p0); - } - { - EXPECT_GE(&array[0], p0); - EXPECT_GE(&array[1], p0); - EXPECT_GE(p0, &array[0]); - - EXPECT_GE(p1, &array[1]); - EXPECT_GE(&array[1], p1); - - auto p2 = p1; - EXPECT_GE(p1, p2); - EXPECT_GE(p1, p0); - } -} - -} // namespace - -TYPED_TEST(CompressedPointerTest, EqualitySamePointerValue) { - auto d = make_pa_unique<Derived>(this->allocator_); - EqualityTest<TestFixture::template PointerType, Base, Base>(d.get()); - EqualityTest<TestFixture::template PointerType, Base, Derived>(d.get()); - EqualityTest<TestFixture::template PointerType, Derived, Base>(d.get()); - EqualityTest<TestFixture::template PointerType, Derived, Derived>(d.get()); -} - -TYPED_TEST(CompressedPointerTest, EqualityDifferentPointerValues) { - auto d = make_pa_unique<DerivedWithMixin>(this->allocator_); - EqualityTest<TestFixture::template PointerType, Mixin, Mixin>(d.get()); - EqualityTest<TestFixture::template PointerType, Mixin, DerivedWithMixin>( - d.get()); - EqualityTest<TestFixture::template PointerType, DerivedWithMixin, Mixin>( - d.get()); - EqualityTest<TestFixture::template PointerType, DerivedWithMixin, - DerivedWithMixin>(d.get()); -} - -TYPED_TEST(CompressedPointerTest, CompareSamePointerValue) { - auto d = make_pa_array_unique<Derived>(this->allocator_, 2); - CompareTest<TestFixture::template PointerType, Base, Base>(d.get()); - CompareTest<TestFixture::template PointerType, Base, Derived>(d.get()); - CompareTest<TestFixture::template PointerType, Derived, Base>(d.get()); - CompareTest<TestFixture::template PointerType, Derived, Derived>(d.get()); -} - -TYPED_TEST(CompressedPointerTest, CompareDifferentPointerValues) { - auto d = make_pa_array_unique<DerivedWithMixin>(this->allocator_, 2); - CompareTest<TestFixture::template PointerType, Mixin, Mixin>(d.get()); - CompareTest<TestFixture::template PointerType, Mixin, DerivedWithMixin>( - d.get()); - CompareTest<TestFixture::template PointerType, DerivedWithMixin, Mixin>( - d.get()); - CompareTest<TestFixture::template PointerType, DerivedWithMixin, - DerivedWithMixin>(d.get()); -} - -} // namespace partition_alloc
diff --git a/base/allocator/partition_allocator/dangling_raw_ptr_checks.cc b/base/allocator/partition_allocator/dangling_raw_ptr_checks.cc deleted file mode 100644 index f58bb80..0000000 --- a/base/allocator/partition_allocator/dangling_raw_ptr_checks.cc +++ /dev/null
@@ -1,75 +0,0 @@ -// 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. - -#include "base/allocator/partition_allocator/dangling_raw_ptr_checks.h" - -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" - -namespace partition_alloc { - -namespace { -DanglingRawPtrDetectedFn* g_dangling_raw_ptr_detected_fn = [](uintptr_t) {}; -DanglingRawPtrReleasedFn* g_dangling_raw_ptr_released_fn = [](uintptr_t) {}; -DanglingRawPtrDetectedFn* g_unretained_dangling_raw_ptr_detected_fn = - [](uintptr_t) {}; -bool g_unretained_dangling_raw_ptr_check_enabled = false; -} // namespace - -DanglingRawPtrDetectedFn* GetDanglingRawPtrDetectedFn() { - PA_DCHECK(g_dangling_raw_ptr_detected_fn); - return g_dangling_raw_ptr_detected_fn; -} - -DanglingRawPtrDetectedFn* GetDanglingRawPtrReleasedFn() { - PA_DCHECK(g_dangling_raw_ptr_released_fn); - return g_dangling_raw_ptr_released_fn; -} - -void SetDanglingRawPtrDetectedFn(DanglingRawPtrDetectedFn fn) { - PA_DCHECK(fn); - g_dangling_raw_ptr_detected_fn = fn; -} - -void SetDanglingRawPtrReleasedFn(DanglingRawPtrReleasedFn fn) { - PA_DCHECK(fn); - g_dangling_raw_ptr_released_fn = fn; -} - -DanglingRawPtrDetectedFn* GetUnretainedDanglingRawPtrDetectedFn() { - return g_unretained_dangling_raw_ptr_detected_fn; -} - -void SetUnretainedDanglingRawPtrDetectedFn(DanglingRawPtrDetectedFn* fn) { - PA_DCHECK(fn); - g_unretained_dangling_raw_ptr_detected_fn = fn; -} - -bool SetUnretainedDanglingRawPtrCheckEnabled(bool enabled) { - bool old = g_unretained_dangling_raw_ptr_check_enabled; - g_unretained_dangling_raw_ptr_check_enabled = enabled; - return old; -} - -namespace internal { - -PA_COMPONENT_EXPORT(PARTITION_ALLOC) void DanglingRawPtrDetected(uintptr_t id) { - g_dangling_raw_ptr_detected_fn(id); -} -PA_COMPONENT_EXPORT(PARTITION_ALLOC) void DanglingRawPtrReleased(uintptr_t id) { - g_dangling_raw_ptr_released_fn(id); -} - -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -void UnretainedDanglingRawPtrDetected(uintptr_t id) { - g_unretained_dangling_raw_ptr_detected_fn(id); -} - -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -bool IsUnretainedDanglingRawPtrCheckEnabled() { - return g_unretained_dangling_raw_ptr_check_enabled; -} - -} // namespace internal -} // namespace partition_alloc
diff --git a/base/allocator/partition_allocator/dangling_raw_ptr_checks.h b/base/allocator/partition_allocator/dangling_raw_ptr_checks.h deleted file mode 100644 index 5c2d305..0000000 --- a/base/allocator/partition_allocator/dangling_raw_ptr_checks.h +++ /dev/null
@@ -1,67 +0,0 @@ -// 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. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_DANGLING_RAW_PTR_CHECKS_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_DANGLING_RAW_PTR_CHECKS_H_ - -#include <cstdint> - -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" - -// When compiled with build flags `enable_dangling_raw_ptr_checks`, dangling -// raw_ptr are reported. Its behavior can be configured here. -// -// Purpose of this level of indirection: -// - Ease testing. -// - Keep partition_alloc/ independent from base/. In most cases, when a -// dangling raw_ptr is detected/released, this involves recording a -// base::debug::StackTrace, which isn't desirable inside partition_alloc/. -// - Be able (potentially) to turn this feature on/off at runtime based on -// dependant's flags. -namespace partition_alloc { - -// DanglingRawPtrDetected is called when there exists a `raw_ptr` referencing a -// memory region and the allocator is asked to release it. -// -// It won't be called again with the same `id`, up until (potentially) a call to -// DanglingRawPtrReleased(`id`) is made. -// -// This function is called from within the allocator, and is not allowed to -// allocate memory. -using DanglingRawPtrDetectedFn = void(uintptr_t /*id*/); -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -DanglingRawPtrDetectedFn* GetDanglingRawPtrDetectedFn(); -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -void SetDanglingRawPtrDetectedFn(DanglingRawPtrDetectedFn); - -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -DanglingRawPtrDetectedFn* GetUnretainedDanglingRawPtrDetectedFn(); -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -void SetUnretainedDanglingRawPtrDetectedFn(DanglingRawPtrDetectedFn*); -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -bool SetUnretainedDanglingRawPtrCheckEnabled(bool enabled); - -// DanglingRawPtrReleased: Called after DanglingRawPtrDetected(id), once the -// last dangling raw_ptr stops referencing the memory region. -// -// This function is allowed to allocate memory. -using DanglingRawPtrReleasedFn = void(uintptr_t /*id*/); -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -DanglingRawPtrReleasedFn* GetDanglingRawPtrReleasedFn(); -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -void SetDanglingRawPtrReleasedFn(DanglingRawPtrReleasedFn); - -namespace internal { - -PA_COMPONENT_EXPORT(PARTITION_ALLOC) void DanglingRawPtrDetected(uintptr_t id); -PA_COMPONENT_EXPORT(PARTITION_ALLOC) void DanglingRawPtrReleased(uintptr_t id); -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -void UnretainedDanglingRawPtrDetected(uintptr_t id); -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -bool IsUnretainedDanglingRawPtrCheckEnabled(); - -} // namespace internal -} // namespace partition_alloc - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_DANGLING_RAW_PTR_CHECKS_H_
diff --git a/base/allocator/partition_allocator/dot/bucket.dot b/base/allocator/partition_allocator/dot/bucket.dot deleted file mode 100644 index c4d6006..0000000 --- a/base/allocator/partition_allocator/dot/bucket.dot +++ /dev/null
@@ -1,60 +0,0 @@ -digraph { - graph[bgcolor=transparent] - node[shape=plaintext] - edge[style=dashed, color=crimson] - - page1[label=< - <table border="0" cellborder="1" cellspacing="0"><tr> - <!-- head partition page --> - <td port="head" bgcolor="darkgrey" width="40" height="52"></td> - <!-- bucket-external memory - not depicted --> - <td width="160"></td> - <!-- a slot span in this bucket --> - <td port="slotspan" bgcolor="crimson" width="80"></td> - <!-- bucket-external memory - not depicted --> - <td width="320"></td> - <!-- tail partition page --> - <td bgcolor="darkgrey" width="40"></td> - </tr></table> - >] - page2[label=< - <table border="0" cellborder="1" cellspacing="0"><tr> - <!-- head partition page --> - <td port="head" bgcolor="darkgrey" width="40" height="52"></td> - <!-- bucket-external memory - not depicted --> - <td width="280"></td> - <!-- a slot span in this bucket --> - <td port="slotspan" bgcolor="crimson" width="80"></td> - <!-- bucket-external memory - not depicted --> - <td width="200"></td> - <!-- tail partition page --> - <td bgcolor="darkgrey" width="40"></td> - </tr></table> - >] - page3[label=< - <table border="0" cellborder="1" cellspacing="0"><tr> - <!-- head partition page --> - <td port="head" bgcolor="darkgrey" width="40" height="52"></td> - <!-- bucket-external memory - not depicted --> - <td width="40"></td> - <!-- a slot span in this bucket --> - <td port="slotspan1" bgcolor="crimson" width="80"></td> - <!-- bucket-external memory - not depicted --> - <td width="120"></td> - <!-- a slot span in this bucket --> - <td port="slotspan2" bgcolor="crimson" width="80"></td> - <!-- bucket-external memory - not depicted --> - <td width="240"></td> - <!-- tail partition page --> - <td bgcolor="darkgrey" width="40"></td> - </tr></table> - >] - - // Invisibly link the head partition pages to force alignment. - page1:head->page2:head->page3:head[style=invis] - - // Inter-super-page links disable constraints so to let the above - // fully control alignment. - page1:slotspan->page2:slotspan->page3:slotspan1[constraint=false] - page3:slotspan1:s->page3:slotspan2:sw -}
diff --git a/base/allocator/partition_allocator/dot/bucket.png b/base/allocator/partition_allocator/dot/bucket.png deleted file mode 100644 index bf7374b..0000000 --- a/base/allocator/partition_allocator/dot/bucket.png +++ /dev/null Binary files differ
diff --git a/base/allocator/partition_allocator/dot/layers.dot b/base/allocator/partition_allocator/dot/layers.dot deleted file mode 100644 index 27ea7c6..0000000 --- a/base/allocator/partition_allocator/dot/layers.dot +++ /dev/null
@@ -1,23 +0,0 @@ -digraph G { - graph[bgcolor=transparent] - node[shape=box,style="filled,rounded",color=deepskyblue] - - subgraph cluster_tc { - label = "Thread Cache" - rankdir = LR - {rank=same;TLS1,TLS2,TLSn} - TLS1->TLS2[style=invisible,dir=none] - TLS2->TLSn[style=dotted,dir=none] - } - - subgraph cluster_central { - label = "Central Allocator (per-partition lock)" - fast[label="slot span freelists (fast path)"] - slow[label="slot span management (slow path)"] - # Forces slow path node beneath fast path node. - fast->slow[style=invisible,dir=none] - } - - # Forces thread-external subgraph beneath thread cache subgraph. - TLS2->fast[style=invisible,dir=none] -}
diff --git a/base/allocator/partition_allocator/dot/layers.png b/base/allocator/partition_allocator/dot/layers.png deleted file mode 100644 index 80c78e2..0000000 --- a/base/allocator/partition_allocator/dot/layers.png +++ /dev/null Binary files differ
diff --git a/base/allocator/partition_allocator/dot/super-page.dot b/base/allocator/partition_allocator/dot/super-page.dot deleted file mode 100644 index 068392d..0000000 --- a/base/allocator/partition_allocator/dot/super-page.dot +++ /dev/null
@@ -1,95 +0,0 @@ -digraph G { - graph[bgcolor=transparent] - node[shape=plaintext] - edge[style=dashed] - - invisible_a[label=< - <TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0"> - <TR> - <TD PORT="red" WIDTH="100"></TD> - <TD PORT="green" WIDTH="20"></TD> - <TD PORT="blue" WIDTH="40"></TD> - <TD PORT="gold" WIDTH="300"></TD> - <TD PORT="pink" WIDTH="60"></TD> - </TR> - </TABLE> - >] - superpage[xlabel="Super Page",label=< - <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" WIDTH="10"> - <TR> - <!-- Head Partition Page --> - <TD BGCOLOR="darkgrey" HEIGHT="52"></TD> - <TD PORT="metadata"></TD> - <TD BGCOLOR="darkgrey" WIDTH="18"></TD> - <!-- Bitmaps --> - <TD WIDTH="100">Bitmaps(?)</TD> - <!-- Several Slot Spans --> - <TD PORT="red" BGCOLOR="crimson" WIDTH="119">3</TD> - <TD PORT="green" BGCOLOR="palegreen" WIDTH="39">1</TD> - <TD PORT="blue" BGCOLOR="cornflowerblue" WIDTH="79">2</TD> - <TD PORT="gold" BGCOLOR="gold" WIDTH="239">6</TD> - <TD PORT="red2" BGCOLOR="crimson" WIDTH="119">3</TD> - <TD PORT="pink" BGCOLOR="deeppink" WIDTH="39">1</TD> - <TD WIDTH="79">...</TD> - <!-- Tail Partition Page --> - <TD BGCOLOR="darkgrey" WIDTH="39"></TD> - </TR> - </TABLE> - >] - invisible_b[label=< - <TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0"> - <TR> - <TD PORT="green" WIDTH="30"></TD> - <TD PORT="blue" WIDTH="60"></TD> - <TD PORT="gold" WIDTH="180"></TD> - <TD PORT="red" WIDTH="90"></TD> - <TD PORT="pink" WIDTH="90"></TD> - </TR> - </TABLE> - >] - metadata_page[xlabel="Metadata",label=< - <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"> - <TR> - <!-- Guard Page Metadata --> - <TD BGCOLOR="darkgrey"> </TD> - <!-- Bitmaps Offset --> - <TD> B? </TD> - <!-- Red Slot Span Metadata --> - <TD BGCOLOR="crimson">v</TD> - <TD BGCOLOR="crimson">+</TD> - <TD BGCOLOR="crimson">+</TD> - <!-- Green Slot Span Metadata --> - <TD BGCOLOR="palegreen">v</TD> - <!-- Blue Slot Span Metadata --> - <TD BGCOLOR="cornflowerblue">v</TD> - <TD BGCOLOR="cornflowerblue">+</TD> - <!-- Gold Slot Span Metadata --> - <TD BGCOLOR="gold">v</TD> - <TD BGCOLOR="gold">+</TD> - <TD BGCOLOR="gold">+</TD> - <TD BGCOLOR="gold">+</TD> - <TD BGCOLOR="gold">+</TD> - <TD BGCOLOR="gold">+</TD> - <!-- Red Slot Span Metadata --> - <TD BGCOLOR="crimson">v</TD> - <TD BGCOLOR="crimson">+</TD> - <TD BGCOLOR="crimson">+</TD> - <!-- Pink Slot Span Metadata --> - <TD BGCOLOR="deeppink">v</TD> - <!-- etc. --> - <TD WIDTH="64">...</TD> - <!-- Guard Page Metadata --> - <TD BGCOLOR="darkgrey"> </TD> - </TR> - </TABLE> - >] - - invisible_a:red->superpage:red->superpage:red2[color=crimson] - superpage:red2->invisible_b:red[color=crimson] - invisible_a:green->superpage:green->invisible_b:green[color=palegreen] - invisible_a:blue->superpage:blue->invisible_b:blue[color=cornflowerblue] - invisible_a:gold->superpage:gold->invisible_b:gold[color=gold] - invisible_a:pink->superpage:pink->invisible_b:pink[color=deeppink] - - superpage:metadata->metadata_page[style="",arrowhead=odot] -}
diff --git a/base/allocator/partition_allocator/dot/super-page.png b/base/allocator/partition_allocator/dot/super-page.png deleted file mode 100644 index 0bfd69a..0000000 --- a/base/allocator/partition_allocator/dot/super-page.png +++ /dev/null Binary files differ
diff --git a/base/allocator/partition_allocator/extended_api.cc b/base/allocator/partition_allocator/extended_api.cc deleted file mode 100644 index cb91858..0000000 --- a/base/allocator/partition_allocator/extended_api.cc +++ /dev/null
@@ -1,134 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/extended_api.h" - -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_config.h" -#include "base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_partition_alloc.h" -#include "base/allocator/partition_allocator/thread_cache.h" - -namespace partition_alloc::internal { - -#if PA_CONFIG(THREAD_CACHE_SUPPORTED) - -namespace { - -void DisableThreadCacheForRootIfEnabled(ThreadSafePartitionRoot* root) { - // Some platforms don't have a thread cache, or it could already have been - // disabled. - if (!root || !root->flags.with_thread_cache) { - return; - } - - ThreadCacheRegistry::Instance().PurgeAll(); - root->flags.with_thread_cache = false; - // Doesn't destroy the thread cache object(s). For background threads, they - // will be collected (and free cached memory) at thread destruction - // time. For the main thread, we leak it. -} - -void EnablePartitionAllocThreadCacheForRootIfDisabled( - ThreadSafePartitionRoot* root) { - if (!root) { - return; - } - root->flags.with_thread_cache = true; -} - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -void DisablePartitionAllocThreadCacheForProcess() { - auto* regular_allocator = - allocator_shim::internal::PartitionAllocMalloc::Allocator(); - auto* aligned_allocator = - allocator_shim::internal::PartitionAllocMalloc::AlignedAllocator(); - DisableThreadCacheForRootIfEnabled(regular_allocator); - if (aligned_allocator != regular_allocator) { - DisableThreadCacheForRootIfEnabled(aligned_allocator); - } - DisableThreadCacheForRootIfEnabled( - allocator_shim::internal::PartitionAllocMalloc::OriginalAllocator()); -} -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - -} // namespace - -#endif // PA_CONFIG(THREAD_CACHE_SUPPORTED) - -ThreadAllocStats GetAllocStatsForCurrentThread() { - ThreadCache* thread_cache = ThreadCache::Get(); - if (ThreadCache::IsValid(thread_cache)) { - return thread_cache->thread_alloc_stats(); - } - return {}; -} - -#if PA_CONFIG(THREAD_CACHE_SUPPORTED) -ThreadCacheProcessScopeForTesting::ThreadCacheProcessScopeForTesting( - ThreadSafePartitionRoot* root) - : root_(root) { -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - auto* regular_allocator = - allocator_shim::internal::PartitionAllocMalloc::Allocator(); - regular_was_enabled_ = - regular_allocator && regular_allocator->flags.with_thread_cache; - - if (root_ != regular_allocator) { - // Another |root| is ThreadCache's PartitionRoot. Need to disable - // thread cache for the process. - DisablePartitionAllocThreadCacheForProcess(); - EnablePartitionAllocThreadCacheForRootIfDisabled(root_); - // Replace ThreadCache's PartitionRoot. - ThreadCache::SwapForTesting(root_); - } else { - if (!regular_was_enabled_) { - EnablePartitionAllocThreadCacheForRootIfDisabled(root_); - ThreadCache::SwapForTesting(root_); - } - } -#else - PA_CHECK(!ThreadCache::IsValid(ThreadCache::Get())); - EnablePartitionAllocThreadCacheForRootIfDisabled(root_); - ThreadCache::SwapForTesting(root_); -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - - PA_CHECK(ThreadCache::Get()); -} - -ThreadCacheProcessScopeForTesting::~ThreadCacheProcessScopeForTesting() { -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - auto* regular_allocator = - allocator_shim::internal::PartitionAllocMalloc::Allocator(); - bool regular_enabled = - regular_allocator && regular_allocator->flags.with_thread_cache; - - if (regular_was_enabled_) { - if (!regular_enabled) { - // Need to re-enable ThreadCache for the process. - EnablePartitionAllocThreadCacheForRootIfDisabled(regular_allocator); - // In the case, |regular_allocator| must be ThreadCache's root. - ThreadCache::SwapForTesting(regular_allocator); - } else { - // ThreadCache is enabled for the process, but we need to be - // careful about ThreadCache's PartitionRoot. If it is different from - // |regular_allocator|, we need to invoke SwapForTesting(). - if (regular_allocator != root_) { - ThreadCache::SwapForTesting(regular_allocator); - } - } - } else { - // ThreadCache for all processes was disabled. - DisableThreadCacheForRootIfEnabled(regular_allocator); - ThreadCache::SwapForTesting(nullptr); - } -#else - // First, disable the test thread cache we have. - DisableThreadCacheForRootIfEnabled(root_); - - ThreadCache::SwapForTesting(nullptr); -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -} -#endif // PA_CONFIG(THREAD_CACHE_SUPPORTED) - -} // namespace partition_alloc::internal
diff --git a/base/allocator/partition_allocator/extended_api.h b/base/allocator/partition_allocator/extended_api.h deleted file mode 100644 index 7146c10..0000000 --- a/base/allocator/partition_allocator/extended_api.h +++ /dev/null
@@ -1,42 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_EXTENDED_API_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_EXTENDED_API_H_ - -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_root.h" -#include "base/allocator/partition_allocator/partition_stats.h" -#include "base/allocator/partition_allocator/thread_cache.h" - -namespace partition_alloc::internal { -// Get allocation stats for the thread cache partition on the current -// thread. See the documentation of ThreadAllocStats for details. -ThreadAllocStats GetAllocStatsForCurrentThread(); - -// Creates a scope for testing which: -// - if the given |root| is a default malloc root for the entire process, -// enables the thread cache for the entire process. -// (This may happen if UsePartitionAllocAsMalloc is enabled.) -// - otherwise, disables the thread cache for the entire process, and -// replaces it with a thread cache for |root|. -// This class is unsafe to run if there are multiple threads running -// in the process. -class ThreadCacheProcessScopeForTesting { - public: - explicit ThreadCacheProcessScopeForTesting(ThreadSafePartitionRoot* root); - ~ThreadCacheProcessScopeForTesting(); - - ThreadCacheProcessScopeForTesting() = delete; - - private: - ThreadSafePartitionRoot* root_ = nullptr; -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - bool regular_was_enabled_ = false; -#endif -}; - -} // namespace partition_alloc::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_EXTENDED_API_H_
diff --git a/base/allocator/partition_allocator/external_builds.md b/base/allocator/partition_allocator/external_builds.md deleted file mode 100644 index 817a012..0000000 --- a/base/allocator/partition_allocator/external_builds.md +++ /dev/null
@@ -1,76 +0,0 @@ -# Chrome-External Builds - -Work is ongoing to make PartitionAlloc a standalone library. The -standalone repository for PartitionAlloc is hosted -[here][standalone-PA-repo]. - -## GN Args - -External clients should examine the args described in -`build_overrides/partition_alloc.gni` and add them in their own source -tree. PartitionAlloc's build will expect them at -`//build_overrides/partition_alloc.gni`. - -In addition, something must provide `build_with_chromium = false` to -the PA build system. - -## `use_partition_alloc` - -The `use_partition_alloc` GN arg, described in -[`build_config.md`](./build_config.md), provides a GN-level seam that -embedders - -1. can set in their GN args and -2. should observe in their GN recipes to conditionally pull in - PartitionAlloc. - -I.E. if you have any reason to disable PartitionAlloc, you should do so -with this GN arg. Avoid pulling in PartitionAlloc headers when the -corresponding buildflag is false. - -Setting `use_partition_alloc` false will also implicitly disable other -features, e.g. nixing the compilation of BackupRefPtr as the -implementation of `raw_ptr<T>`. - -## Periodic Memory Reduction Routines - -PartitionAlloc provides APIs to - -* reclaim memory (see [memory\_reclaimer.h](./memory_reclaimer.h)) and - -* purge thread caches (see [thread\_cache.h](./thread_cache.h)). - -Both of these must be called by the embedder external to PartitionAlloc. -PA provides neither an event loop nor timers of its own, delegating this -to its clients. - -## Build Considerations - -External clients create constraints on PartitionAlloc's implementation. - -### C++17 - -PartitionAlloc targets C++17. This is aligned with our first external -client, PDFium, and may be further constrained by other clients. These -impositions prevent us from moving in lockstep with Chrome's target -C++ version. - -We do not even have guarantees of backported future features, e.g. -C++20's designated initializers. Therefore, these cannot ship with -PartitionAlloc. - -### MSVC Support - -PDFium supports MSVC. PartitionAlloc will have to match it. - -### MSVC Constraint: No Inline Assembly - -MSVC's syntax for `asm` blocks differs from the one widely adopted in -parts of Chrome. But more generally, -[MSVC doesn't support inline assembly on ARM and x64 processors][msvc-inline-assembly]. -Assembly blocks should be gated behind compiler-specific flags and -replaced with intrinsics in the presence of `COMPILER_MSVC` (absent -`__clang__`). - -[standalone-PA-repo]: https://chromium.googlesource.com/chromium/src/base/allocator/partition_allocator.git -[msvc-inline-assembly]: https://docs.microsoft.com/en-us/cpp/assembler/inline/inline-assembler?view=msvc-170
diff --git a/base/allocator/partition_allocator/freeslot_bitmap.h b/base/allocator/partition_allocator/freeslot_bitmap.h deleted file mode 100644 index 56ab449..0000000 --- a/base/allocator/partition_allocator/freeslot_bitmap.h +++ /dev/null
@@ -1,140 +0,0 @@ -// 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. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_FREESLOT_BITMAP_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_FREESLOT_BITMAP_H_ - -#include <climits> -#include <cstdint> -#include <utility> - -#include "base/allocator/partition_allocator/freeslot_bitmap_constants.h" -#include "base/allocator/partition_allocator/partition_alloc_base/bits.h" -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" - -#if BUILDFLAG(USE_FREESLOT_BITMAP) - -namespace partition_alloc::internal { - -PA_ALWAYS_INLINE uintptr_t GetFreeSlotBitmapAddressForPointer(uintptr_t ptr) { - uintptr_t super_page = ptr & kSuperPageBaseMask; - return SuperPageFreeSlotBitmapAddr(super_page); -} - -// Calculates the cell address and the offset inside the cell corresponding to -// the |slot_start|. -PA_ALWAYS_INLINE std::pair<FreeSlotBitmapCellType*, size_t> -GetFreeSlotBitmapCellPtrAndBitIndex(uintptr_t slot_start) { - uintptr_t slot_superpage_offset = slot_start & kSuperPageOffsetMask; - uintptr_t superpage_bitmap_start = - GetFreeSlotBitmapAddressForPointer(slot_start); - uintptr_t cell_addr = base::bits::AlignDown( - superpage_bitmap_start + - (slot_superpage_offset / kSmallestBucket) / CHAR_BIT, - sizeof(FreeSlotBitmapCellType)); - PA_DCHECK(cell_addr < superpage_bitmap_start + kFreeSlotBitmapSize); - size_t bit_index = - (slot_superpage_offset / kSmallestBucket) & kFreeSlotBitmapOffsetMask; - PA_DCHECK(bit_index < kFreeSlotBitmapBitsPerCell); - return {reinterpret_cast<FreeSlotBitmapCellType*>(cell_addr), bit_index}; -} - -// This bitmap marks the used slot as 0 and free one as 1. This is because we -// would like to set all the slots as "used" by default to prevent allocating a -// used slot when the freelist entry is overwritten. The state of the bitmap is -// expected to be synced with freelist (i.e. the bitmap is set to 1 if and only -// if the slot is in the freelist). - -PA_ALWAYS_INLINE FreeSlotBitmapCellType CellWithAOne(size_t n) { - return static_cast<FreeSlotBitmapCellType>(1) << n; -} - -PA_ALWAYS_INLINE FreeSlotBitmapCellType CellWithTrailingOnes(size_t n) { - return (static_cast<FreeSlotBitmapCellType>(1) << n) - - static_cast<FreeSlotBitmapCellType>(1); -} - -// Returns true if the bit corresponding to |slot_start| is used( = 0) -PA_ALWAYS_INLINE bool FreeSlotBitmapSlotIsUsed(uintptr_t slot_start) { - auto [cell, bit_index] = GetFreeSlotBitmapCellPtrAndBitIndex(slot_start); - return (*cell & CellWithAOne(bit_index)) == 0; -} - -// Mark the bit corresponding to |slot_start| as used( = 0). -PA_ALWAYS_INLINE void FreeSlotBitmapMarkSlotAsUsed(uintptr_t slot_start) { - PA_CHECK(!FreeSlotBitmapSlotIsUsed(slot_start)); - auto [cell, bit_index] = GetFreeSlotBitmapCellPtrAndBitIndex(slot_start); - *cell &= ~CellWithAOne(bit_index); -} - -// Mark the bit corresponding to |slot_start| as free( = 1). -PA_ALWAYS_INLINE void FreeSlotBitmapMarkSlotAsFree(uintptr_t slot_start) { - PA_CHECK(FreeSlotBitmapSlotIsUsed(slot_start)); - auto [cell, bit_index] = GetFreeSlotBitmapCellPtrAndBitIndex(slot_start); - *cell |= CellWithAOne(bit_index); -} - -// Resets (= set to 0) all the bits corresponding to the slot-start addresses -// within [begin_addr, end_addr). |begin_addr| has to be the beginning of a -// slot, but |end_addr| does not. -PA_ALWAYS_INLINE void FreeSlotBitmapReset(uintptr_t begin_addr, - uintptr_t end_addr, - uintptr_t slot_size) { - PA_DCHECK(begin_addr <= end_addr); - // |end_addr| has to be kSmallestBucket-aligned. - PA_DCHECK((end_addr & (kSmallestBucket - 1)) == 0u); - for (uintptr_t slot_start = begin_addr; slot_start < end_addr; - slot_start += slot_size) { - auto [cell, bit_index] = GetFreeSlotBitmapCellPtrAndBitIndex(slot_start); - *cell &= ~CellWithAOne(bit_index); - } - -#if BUILDFLAG(PA_DCHECK_IS_ON) - // Checks if the cells that are meant to contain only unset bits are really 0. - auto [begin_cell, begin_bit_index] = - GetFreeSlotBitmapCellPtrAndBitIndex(begin_addr); - auto [end_cell, end_bit_index] = - GetFreeSlotBitmapCellPtrAndBitIndex(end_addr); - - // The bits that should be marked to 0 are |begin_bit_index|th bit of - // |begin_cell| to |end_bit_index - 1|th bit of |end_cell|. We verify all the - // bits are set to 0 for the cells between [begin_cell + 1, end_cell). For the - // |begin_cell| and |end_cell|, we have to handle them separately to only - // check the partial bits. - // | begin_cell | |...| | end_cell | - // |11...100...0|0...0|...|0...0|0...01...1| - // ^ ^ - // | | - // begin_addr end_addr - - if (begin_cell == end_cell) { - PA_DCHECK((*begin_cell & (~CellWithTrailingOnes(begin_bit_index) & - CellWithTrailingOnes(end_bit_index))) == 0u); - } - - if (begin_bit_index != 0) { - // Checks the bits between [begin_bit_index, kFreeSlotBitmapBitsPerCell) in - // the begin_cell are 0 - PA_DCHECK((*begin_cell & ~CellWithTrailingOnes(begin_bit_index)) == 0u); - ++begin_cell; - } - - if (end_bit_index != 0) { - // Checks the bits between [0, end_bit_index) in the end_cell are 0 - PA_DCHECK((*end_cell & CellWithTrailingOnes(end_bit_index)) == 0u); - } - - for (FreeSlotBitmapCellType* cell = begin_cell; cell != end_cell; ++cell) { - PA_DCHECK(*cell == 0u); - } -#endif // BUILDFLAG(PA_DCHECK_IS_ON) -} - -} // namespace partition_alloc::internal - -#endif // BUILDFLAG(USE_FREESLOT_BITMAP) - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_FREESLOT_BITMAP_H_ \ No newline at end of file
diff --git a/base/allocator/partition_allocator/freeslot_bitmap_constants.h b/base/allocator/partition_allocator/freeslot_bitmap_constants.h deleted file mode 100644 index 99768d7..0000000 --- a/base/allocator/partition_allocator/freeslot_bitmap_constants.h +++ /dev/null
@@ -1,61 +0,0 @@ -// 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. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_FREESLOT_BITMAP_CONSTANTS_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_FREESLOT_BITMAP_CONSTANTS_H_ - -#include <cstdint> - -#include "base/allocator/partition_allocator/partition_alloc_base/bits.h" -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "base/allocator/partition_allocator/partition_alloc_forward.h" -#include "base/allocator/partition_allocator/reservation_offset_table.h" - -namespace partition_alloc::internal { - -using FreeSlotBitmapCellType = uint64_t; -constexpr size_t kFreeSlotBitmapBitsPerCell = - sizeof(FreeSlotBitmapCellType) * CHAR_BIT; -constexpr size_t kFreeSlotBitmapOffsetMask = kFreeSlotBitmapBitsPerCell - 1; - -// The number of bits necessary for the bitmap is equal to the maximum number of -// slots in a super page. -constexpr size_t kFreeSlotBitmapSize = - (kSuperPageSize / kSmallestBucket) / CHAR_BIT; - -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t -ReservedFreeSlotBitmapSize() { -#if BUILDFLAG(USE_FREESLOT_BITMAP) - return base::bits::AlignUp(kFreeSlotBitmapSize, PartitionPageSize()); -#else - return 0; -#endif -} - -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t -CommittedFreeSlotBitmapSize() { -#if BUILDFLAG(USE_FREESLOT_BITMAP) - return base::bits::AlignUp(kFreeSlotBitmapSize, SystemPageSize()); -#else - return 0; -#endif -} - -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t -NumPartitionPagesPerFreeSlotBitmap() { - return ReservedFreeSlotBitmapSize() / PartitionPageSize(); -} - -#if BUILDFLAG(USE_FREESLOT_BITMAP) -PA_ALWAYS_INLINE uintptr_t SuperPageFreeSlotBitmapAddr(uintptr_t super_page) { - PA_DCHECK(!(super_page % kSuperPageAlignment)); - return super_page + PartitionPageSize(); -} -#endif - -} // namespace partition_alloc::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_FREESLOT_BITMAP_CONSTANTS_H_
diff --git a/base/allocator/partition_allocator/freeslot_bitmap_unittest.cc b/base/allocator/partition_allocator/freeslot_bitmap_unittest.cc deleted file mode 100644 index 2edd94d..0000000 --- a/base/allocator/partition_allocator/freeslot_bitmap_unittest.cc +++ /dev/null
@@ -1,165 +0,0 @@ -// 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. - -#include "base/allocator/partition_allocator/freeslot_bitmap.h" - -#include <cstdint> -#include <limits> - -#include "base/allocator/partition_allocator/freeslot_bitmap_constants.h" -#include "base/allocator/partition_allocator/partition_alloc.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "base/allocator/partition_allocator/partition_alloc_forward.h" -#include "base/allocator/partition_allocator/partition_page.h" -#include "testing/gtest/include/gtest/gtest.h" - -// This test is disabled when MEMORY_TOOL_REPLACES_ALLOCATOR is defined because -// we cannot locate the freeslot bitmap address in that case. -#if BUILDFLAG(USE_FREESLOT_BITMAP) && !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - -namespace partition_alloc::internal { - -namespace { - -class PartitionAllocFreeSlotBitmapTest : public ::testing::Test { - protected: - static constexpr FreeSlotBitmapCellType kAllUsed = 0u; - static constexpr FreeSlotBitmapCellType kAllFree = - std::numeric_limits<FreeSlotBitmapCellType>::max(); - - void SetUp() override { - // Allocates memory and creates a pseudo superpage in it. We need to - // allocate |2 * kSuperPageSize| so that a whole superpage is contained in - // the allocated region. - allocator_.init({ - PartitionOptions::AlignedAlloc::kDisallowed, - PartitionOptions::ThreadCache::kDisabled, - PartitionOptions::Quarantine::kDisallowed, - PartitionOptions::Cookie::kAllowed, - PartitionOptions::BackupRefPtr::kDisabled, - PartitionOptions::BackupRefPtrZapping::kDisabled, - PartitionOptions::UseConfigurablePool::kNo, - }); - allocated_ptr_ = reinterpret_cast<uintptr_t>( - allocator_.root()->Alloc(2 * kSuperPageSize, "")); - super_page_ = (allocated_ptr_ + kSuperPageSize) & kSuperPageBaseMask; - - // Checks that the whole superpage is in the allocated region. - PA_DCHECK(super_page_ + kSuperPageSize <= - allocated_ptr_ + 2 * kSuperPageSize); - } - - void TearDown() override { - allocator_.root()->Free(reinterpret_cast<void*>(allocated_ptr_)); - } - - // Returns the |index|-th slot address in the virtual superpage. It assumes - // that there are no slot spans and the superpage is only filled with the slot - // of size |kSmallestBucket|. - uintptr_t SlotAddr(size_t index) { - return SuperPagePayloadBegin(super_page_, false) + index * kSmallestBucket; - } - - // Returns the last slot address in the virtual superpage. It assumes that - // there are no slot spans but the superpage is only filled with the slot of - // size |kSmallestBucket|. - uintptr_t LastSlotAddr() { - return super_page_ + kSuperPageSize - PartitionPageSize() - kSmallestBucket; - } - - private: - uintptr_t allocated_ptr_; - uintptr_t super_page_; - PartitionAllocator<ThreadSafe> allocator_; -}; - -} // namespace - -TEST_F(PartitionAllocFreeSlotBitmapTest, MarkFirstSlotAsUsed) { - uintptr_t slot_addr = SlotAddr(0); - FreeSlotBitmapMarkSlotAsFree(slot_addr); - EXPECT_FALSE(FreeSlotBitmapSlotIsUsed(slot_addr)); - - FreeSlotBitmapMarkSlotAsUsed(slot_addr); - EXPECT_TRUE(FreeSlotBitmapSlotIsUsed(slot_addr)); -} - -TEST_F(PartitionAllocFreeSlotBitmapTest, MarkFirstSlotAsFree) { - uintptr_t slot_addr = SlotAddr(0); - // All slots are set to "used" by default. - EXPECT_TRUE(FreeSlotBitmapSlotIsUsed(slot_addr)); - - FreeSlotBitmapMarkSlotAsFree(slot_addr); - EXPECT_FALSE(FreeSlotBitmapSlotIsUsed(slot_addr)); -} - -TEST_F(PartitionAllocFreeSlotBitmapTest, MarkAllBitsInCellAsUsed) { - const size_t kFirstSlotAddr = SlotAddr(0); - const size_t kLastSlotAddr = SlotAddr(kFreeSlotBitmapBitsPerCell); - - auto [cell_first_slot, bit_index_first_slot] = - GetFreeSlotBitmapCellPtrAndBitIndex(kFirstSlotAddr); - auto [cell_last_slot, bit_index_last_slot] = - GetFreeSlotBitmapCellPtrAndBitIndex(kLastSlotAddr); - - // Check that the bit corresponding to |kFirstSlotAddr| is the first bit in - // some cell (= |cell_first_slot|), and the bit for |kLastSlotAddr| is the - // first bit in the next cell. This means that we are manipulating all the - // bits in |cell_first_slot| in this test. - EXPECT_EQ(0u, bit_index_first_slot); - EXPECT_EQ(0u, bit_index_last_slot); - EXPECT_NE(cell_first_slot, cell_last_slot); - - for (size_t slot_addr = kFirstSlotAddr; slot_addr < kLastSlotAddr; - slot_addr += kSmallestBucket) { - FreeSlotBitmapMarkSlotAsFree(slot_addr); - } - - // Check all the bits in |cell_first_slot| are 1 (= free). - EXPECT_EQ(kAllFree, *cell_first_slot); - - for (size_t slot_addr = kFirstSlotAddr; slot_addr < kLastSlotAddr; - slot_addr += kSmallestBucket) { - FreeSlotBitmapMarkSlotAsUsed(slot_addr); - } - - // Check all the bits in |cell_first_slot| are 0 (= used). - EXPECT_EQ(kAllUsed, *cell_first_slot); -} - -TEST_F(PartitionAllocFreeSlotBitmapTest, MarkLastSlotAsUsed) { - uintptr_t last_slot_addr = LastSlotAddr(); - FreeSlotBitmapMarkSlotAsFree(last_slot_addr); - EXPECT_FALSE(FreeSlotBitmapSlotIsUsed(last_slot_addr)); - - FreeSlotBitmapMarkSlotAsUsed(last_slot_addr); - EXPECT_TRUE(FreeSlotBitmapSlotIsUsed(last_slot_addr)); -} - -TEST_F(PartitionAllocFreeSlotBitmapTest, ResetBitmap) { - const size_t kNumSlots = 3 * kFreeSlotBitmapBitsPerCell; - for (size_t i = 0; i < kNumSlots; ++i) { - FreeSlotBitmapMarkSlotAsFree(SlotAddr(i)); - } - - auto [cell_first_slot, bit_index_first_slot] = - GetFreeSlotBitmapCellPtrAndBitIndex(SlotAddr(0)); - EXPECT_EQ(0u, bit_index_first_slot); - EXPECT_EQ(kAllFree, *cell_first_slot); - EXPECT_EQ(kAllFree, *(cell_first_slot + 1)); - EXPECT_EQ(kAllFree, *(cell_first_slot + 2)); - - FreeSlotBitmapReset(SlotAddr(kFreeSlotBitmapBitsPerCell), - SlotAddr(2 * kFreeSlotBitmapBitsPerCell), - kSmallestBucket); - EXPECT_EQ(kAllFree, *cell_first_slot); - EXPECT_EQ(kAllUsed, *(cell_first_slot + 1)); - EXPECT_EQ(kAllFree, *(cell_first_slot + 2)); -} - -} // namespace partition_alloc::internal - -#endif // BUILDFLAG(USE_FREESLOT_BITMAP) && - // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
diff --git a/base/allocator/partition_allocator/glossary.md b/base/allocator/partition_allocator/glossary.md deleted file mode 100644 index 34fd688..0000000 --- a/base/allocator/partition_allocator/glossary.md +++ /dev/null
@@ -1,182 +0,0 @@ -# Glossary - -This page describes some core terminology used in PartitionAlloc. -A weak attempt is made to present terms "in conceptual order" s.t. -each term depends mainly upon previously defined ones. - -* **Partition**: A heap that is separated and protected both from other - partitions and from non-PartitionAlloc memory. Each partition holds - multiple buckets. - -*** promo -**NOTE**: In code (and comments), "partition," "root," and even -"allocator" are all conceptually the same thing. -*** - -## Pages - -* **System Page**: A memory page defined by the CPU/OS. Commonly - referred to as a "virtual page" in other contexts. This is typically - 4KiB, but it can be larger. PartitionAlloc supports up to 64KiB, - though this constant isn't always known at compile time (depending - on the OS). -* **Partition Page**: The most common granularity used by - PartitionAlloc. Consists of exactly 4 system pages. -* **Super Page**: A 2MiB region, aligned on a 2MiB boundary. Not to - be confused with OS-level terms like "large page" or "huge page", - which are also commonly 2MiB. These have to be fully committed / - uncommitted in memory, whereas super pages can be partially committed - with system page granularity. -* **Extent**: An extent is a run of consecutive super pages (belonging - to a single partition). Extents are to super pages what slot spans are - to slots (see below). - -## Slots and Spans - -* **Slot**: An indivisible allocation unit. Slot sizes are tied to - buckets. For example, each allocation that falls into the bucket - (224, 256] would be satisfied with a slot of size 256. This - applies only to normal buckets, not to direct map. -* **Slot Span**: A run of same-sized slots that are contiguous in - memory. Slot span size is a multiple of partition page size, but it - isn't always a multiple of slot size, although we try hard for this - to be the case. - * **Small Bucket**: Allocations up to 4 partition pages. In these - cases, slot spans are always between 1 and 4 partition pages in - size. For each slot span size, the slot span is chosen to minimize - number of pages used while keeping the rounding waste under a - reasonable limit. - * For example, for a slot size 96, 64B waste is deemed acceptable - when using a single partition page, but for slot size - 384, the potential waste of 256B wouldn't be, so 3 partition pages - are used to achieve 0B waste. - * PartitionAlloc may avoid waste by lowering the number of committed - system pages compared to the number of reserved pages. For - example, for the slot size of 896B we'd use a slot span of 2 - partition pages of 16KiB, i.e. 8 system pages of 4KiB, but commit - only up to 7, thus resulting in perfect packing. - * **Single-Slot Span**: Allocations above 4 partition pages (but - ≤`kMaxBucketed`). This is because each slot span is guaranteed to - hold exactly one slot. - * Fun fact: there are sizes ≤4 partition pages that result in a - slot span having exactly 1 slot, but nonetheless they're still - classified as small buckets. The reason is that single-slot spans - are often handled by a different code path, and that distinction - is made purely based on slot size, for simplicity and efficiency. - -## Buckets - -* **Bucket**: A collection of regions in a partition that contains - similar-sized objects. For example, one bucket may hold objects of - size (224, 256], another (256, 320], etc. Bucket size - brackets are geometrically spaced, - [going up to `kMaxBucketed`][max-bucket-comment]. - * Plainly put, all slots (ergo the resulting spans) of a given size - class are logically chained into one bucket. - - - -* **Normal Bucket**: Any bucket whose size ceiling does not exceed - `kMaxBucketed`. This is the common case in PartitionAlloc, and - the "normal" modifier is often dropped in casual reference. -* **Direct Map (Bucket)**: Any allocation whose size exceeds `kMaxBucketed`. - -## Other Terms - -* **Object**: A chunk of memory returned to the allocating invoker - of the size requested. It doesn't have to span the entire slot, - nor does it have to begin at the slot start. This term is commonly - used as a parameter name in PartitionAlloc code, as opposed to - `slot_start`. -* **Thread Cache**: A [thread-local structure][pa-thread-cache] that - holds some not-too-large memory chunks, ready to be allocated. This - speeds up in-thread allocation by reducing a lock hold to a - thread-local storage lookup, improving cache locality. -* **Pool**: A large (and contiguous on 64-bit) virtual address region, housing - super pages, etc. from which PartitionAlloc services allocations. The - primary purpose of the pools is to provide a fast answer to the - question, "Did PartitionAlloc allocate the memory for this pointer - from this pool?" with a single bit-masking operation. - * The regular pool is a general purpose pool that contains allocations that - aren't protected by BackupRefPtr. - * The BRP pool contains all allocations protected by BackupRefPtr. - * [64-bit only] The configurable pool is named generically, because its - primary user (the [V8 Sandbox][v8-sandbox]) can configure it at runtime, - providing a pre-existing mapping. Its allocations aren't protected by - BackupRefPtr. - * [64-bit only] The pkey pool is returning memory tagged with a memory - protection key on supported platforms. It's primary user is [V8 CFI][v8-cfi]. - -*** promo -Pools are downgraded into a logical concept in 32-bit environments, -tracking a non-contiguous set of allocations using a bitmap. -*** - -* **Payload**: The usable area of a super page in which slot spans - reside. While generally this means "everything between the first - and last guard partition pages in a super page," the presence of - other metadata (e.g. StarScan bitmaps) can bump the starting offset - forward. While this term is entrenched in the code, the team - considers it suboptimal and is actively looking for a replacement. -* **Allocation Fast Path**: A path taken during an allocation that is - considered fast. Usually means that an allocation request can be - immediately satisfied by grabbing a slot from the freelist of the - first active slot span in the bucket. -* **Allocation Slow Path**: Anything which is not fast (see above). - Can involve - * finding another active slot span in the list, - * provisioning more slots in a slot span, - * bringing back a free (or decommitted) slot span, - * allocating a new slot span, or even - * allocating a new super page. - -*** aside -By "slow" we may mean something as simple as extra logic (`if` -statements etc.), or something as costly as system calls. -*** - -## Legacy Terms - -These terms are (mostly) deprecated and should not be used. They are -surfaced here to provide a ready reference for readers coming from -older design documents or documentation. - -* **GigaCage**: A memory region several gigabytes wide, reserved by - PartitionAlloc upon initialization, from which nearly all allocations - are taken. _Pools_ have overtaken GigaCage in conceptual importance, - and so and so there is less need today to refer to "GigaCage" or the - "cage." This is especially true given the V8 Sandbox and the - configurable pool (see above). - -## PartitionAlloc-Everywhere - -Originally, PartitionAlloc was used only in Blink (Chromium's rendering engine). -It was invoked explicitly, by calling PartitionAlloc APIs directly. - -PartitionAlloc-Everywhere is the name of the project that brought PartitionAlloc -to the entire-ish codebase (exclusions apply). This was done by intercepting -`malloc()`, `free()`, `realloc()`, aforementioned `posix_memalign()`, etc. and -routing them into PartitionAlloc. The shim located in -`base/allocator/allocator_shim_default_dispatch_to_partition_alloc.h` is -responsible for intercepting. For more details, see -[base/allocator/README.md](../../../base/allocator/README.md). - -A special, catch-it-all *Malloc* partition has been created for the intercepted -`malloc()` et al. This is to isolate from already existing Blink partitions. -The only exception from that is Blink's *FastMalloc* partition, which was also -catch-it-all in nature, so it's perfectly fine to merge these together, to -minimize fragmentation. - -As of 2022, PartitionAlloc-Everywhere is supported on - -* Windows 32- and 64-bit -* Linux -* Android 32- and 64-bit -* macOS -* Fuchsia - -[max-bucket-comment]: https://source.chromium.org/chromium/chromium/src/+/main:base/allocator/partition_allocator/partition_alloc_constants.h;l=345;drc=667e6b001f438521e1c1a1bc3eabeead7aaa1f37 -[pa-thread-cache]: https://source.chromium.org/chromium/chromium/src/+/main:base/allocator/partition_allocator/thread_cache.h -[v8-sandbox]: https://docs.google.com/document/d/1FM4fQmIhEqPG8uGp5o9A-mnPB5BOeScZYpkHjo0KKA8/preview# -[v8-cfi]: https://docs.google.com/document/d/1O2jwK4dxI3nRcOJuPYkonhTkNQfbmwdvxQMyXgeaRHo/preview#
diff --git a/base/allocator/partition_allocator/gwp_asan_support.cc b/base/allocator/partition_allocator/gwp_asan_support.cc deleted file mode 100644 index 878ef66..0000000 --- a/base/allocator/partition_allocator/gwp_asan_support.cc +++ /dev/null
@@ -1,135 +0,0 @@ -// 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. - -#include "base/allocator/partition_allocator/gwp_asan_support.h" - -#if BUILDFLAG(ENABLE_GWP_ASAN_SUPPORT) - -#include "base/allocator/partition_allocator/freeslot_bitmap_constants.h" -#include "base/allocator/partition_allocator/page_allocator_constants.h" -#include "base/allocator/partition_allocator/partition_alloc_base/no_destructor.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/partition_bucket.h" -#include "base/allocator/partition_allocator/partition_lock.h" -#include "base/allocator/partition_allocator/partition_page.h" -#include "base/allocator/partition_allocator/partition_ref_count.h" -#include "base/allocator/partition_allocator/partition_root.h" -#include "build/build_config.h" - -namespace partition_alloc { - -// static -void* GwpAsanSupport::MapRegion(size_t slot_count, - std::vector<uint16_t>& free_list) { - PA_CHECK(slot_count > 0); - - constexpr PartitionOptions kConfig{ - PartitionOptions::AlignedAlloc::kDisallowed, - PartitionOptions::ThreadCache::kDisabled, - PartitionOptions::Quarantine::kDisallowed, - PartitionOptions::Cookie::kDisallowed, - PartitionOptions::BackupRefPtr::kEnabled, - PartitionOptions::BackupRefPtrZapping::kDisabled, - PartitionOptions::UseConfigurablePool::kNo, - }; - static internal::base::NoDestructor<ThreadSafePartitionRoot> root(kConfig); - - const size_t kSlotSize = 2 * internal::SystemPageSize(); - uint16_t bucket_index = - PartitionRoot<internal::ThreadSafe>::SizeToBucketIndex( - kSlotSize, root->GetBucketDistribution()); - auto* bucket = root->buckets + bucket_index; - - const size_t kSuperPagePayloadStartOffset = - internal::SuperPagePayloadStartOffset( - /* is_managed_by_normal_buckets = */ true, - /* with_quarantine = */ false); - PA_CHECK(kSuperPagePayloadStartOffset % kSlotSize == 0); - const size_t kSuperPageGwpAsanSlotAreaBeginOffset = - kSuperPagePayloadStartOffset; - const size_t kSuperPageGwpAsanSlotAreaEndOffset = - internal::SuperPagePayloadEndOffset(); - const size_t kSuperPageGwpAsanSlotAreaSize = - kSuperPageGwpAsanSlotAreaEndOffset - kSuperPageGwpAsanSlotAreaBeginOffset; - const size_t kSlotsPerSlotSpan = bucket->get_bytes_per_span() / kSlotSize; - const size_t kSlotsPerSuperPage = - kSuperPageGwpAsanSlotAreaSize / (kSlotsPerSlotSpan * kSlotSize); - - size_t super_page_count = 1 + ((slot_count - 1) / kSlotsPerSuperPage); - PA_CHECK(super_page_count <= - std::numeric_limits<size_t>::max() / kSuperPageSize); - uintptr_t super_page_span_start; - { - internal::ScopedGuard locker{root->lock_}; - super_page_span_start = bucket->AllocNewSuperPageSpanForGwpAsan( - root.get(), super_page_count, 0); - - if (!super_page_span_start) { - return nullptr; - } - -#if defined(ARCH_CPU_64_BITS) - // Mapping the GWP-ASan region in to the lower 32-bits of address space - // makes it much more likely that a bad pointer dereference points into - // our region and triggers a false positive report. We rely on the fact - // that PA address pools are never allocated in the first 4GB due to - // their alignment requirements. - PA_CHECK(super_page_span_start >= (1ULL << 32)); -#endif // defined(ARCH_CPU_64_BITS) - - uintptr_t super_page_span_end = - super_page_span_start + super_page_count * kSuperPageSize; - PA_CHECK(super_page_span_start < super_page_span_end); - - for (uintptr_t super_page = super_page_span_start; - super_page < super_page_span_end; super_page += kSuperPageSize) { - auto* page_metadata = - internal::PartitionSuperPageToMetadataArea<internal::ThreadSafe>( - super_page); - - // Index 0 is invalid because it is the super page extent metadata. - for (size_t partition_page_idx = - 1 + internal::NumPartitionPagesPerFreeSlotBitmap(); - partition_page_idx + bucket->get_pages_per_slot_span() < - internal::NumPartitionPagesPerSuperPage(); - partition_page_idx += bucket->get_pages_per_slot_span()) { - auto* slot_span_metadata = - &page_metadata[partition_page_idx].slot_span_metadata; - bucket->InitializeSlotSpanForGwpAsan(slot_span_metadata); - auto slot_span_start = - internal::SlotSpanMetadata<internal::ThreadSafe>::ToSlotSpanStart( - slot_span_metadata); - - for (uintptr_t slot_idx = 0; slot_idx < kSlotsPerSlotSpan; ++slot_idx) { - auto slot_start = slot_span_start + slot_idx * kSlotSize; - internal::PartitionRefCountPointer(slot_start)->InitalizeForGwpAsan(); - size_t global_slot_idx = (slot_start - super_page_span_start - - kSuperPageGwpAsanSlotAreaBeginOffset) / - kSlotSize; - PA_DCHECK(global_slot_idx < std::numeric_limits<uint16_t>::max()); - free_list.push_back(global_slot_idx); - if (free_list.size() == slot_count) { - return reinterpret_cast<void*>( - super_page_span_start + kSuperPageGwpAsanSlotAreaBeginOffset - - internal::SystemPageSize()); // Depends on the PA guard region - // in front of the super page - // payload area. - } - } - } - } - } - - PA_NOTREACHED(); - return nullptr; -} - -// static -bool GwpAsanSupport::CanReuse(uintptr_t slot_start) { - return internal::PartitionRefCountPointer(slot_start)->CanBeReusedByGwpAsan(); -} - -} // namespace partition_alloc - -#endif // BUILDFLAG(ENABLE_GWP_ASAN_SUPPORT)
diff --git a/base/allocator/partition_allocator/gwp_asan_support.h b/base/allocator/partition_allocator/gwp_asan_support.h deleted file mode 100644 index dd1f96e..0000000 --- a/base/allocator/partition_allocator/gwp_asan_support.h +++ /dev/null
@@ -1,120 +0,0 @@ -// 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. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_GWP_ASAN_SUPPORT_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_GWP_ASAN_SUPPORT_H_ - -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" - -#if BUILDFLAG(ENABLE_GWP_ASAN_SUPPORT) - -#include <cstddef> -#include <cstdint> -#include <vector> - -namespace partition_alloc { - -// This class allows GWP-ASan allocations to be backed by PartitionAlloc and, -// consequently, protected by MiraclePtr. -// -// GWP-ASan mainly operates at the system memory page granularity. During -// process startup, it reserves a certain number of consecutive system pages. -// -// The standard layout is as follows: -// -// +-------------------+-------- -// | | â–² â–² -// | system page 0 |(a) (c) -// | | â–¼ â–¼ -// +-------------------+-------- -// | | â–² â–² -// | system page 1 |(b) | -// | | â–¼ | -// +-------------------+--- (d) (a) inaccessible -// | | â–² | (b) accessible -// | system page 2 |(a) | (c) initial guard page -// | | â–¼ â–¼ (d) allocation slot -// +-------------------+-------- -// | | â–² â–² -// | system page 3 |(b) | -// | | â–¼ | -// +-------------------+--- (d) -// | | â–² | -// | system page 4 |(a) | -// | | â–¼ â–¼ -// |-------------------|-------- -// | | â–² â–² -// | ... |(a) (d) -// -// Unfortunately, PartitionAlloc can't provide GWP-ASan an arbitrary number of -// consecutive allocation slots. Allocations need to be grouped into 2MB super -// pages so that the allocation metadata can be easily located. -// -// Below is the new layout: -// -// +----------------------------------- -// | | â–² â–² -// | system page 0 | | | -// | | | | -// +-------------------+ | | -// | | | | -// | ... | (e) | -// | | | | -// +-------------------+------- | | -// | | â–² â–² | | -// | system page k-1 |(a) (c) | | -// | | â–¼ â–¼ â–¼ | -// +-------------------+----------- (f) -// | | â–² â–² | -// | system page k |(b) | | -// | | â–¼ | | -// +-------------------+--- (d) | -// | | â–² | | -// | system page k+1 |(a) | | -// | | â–¼ â–¼ | -// +-------------------+----------- | -// | | | (a) inaccessible -// | ... | | (b) accessible -// | | â–¼ (c) initial guard page -// +----------------------------------- (d) allocation slot -// | | â–² â–² (e) super page metadata -// | system page m | | | (f) super page -// | | | | (g) pseudo allocation slot -// +-------------------+------- | | -// | | â–² | | -// | ... | | (e) | -// | | | | | -// +-------------------+--- (g) | | -// | | â–² | | | -// | system page m+k-1 |(a) | | | -// | | â–¼ â–¼ â–¼ | -// +-------------------+----------- (f) -// | | â–² â–² | -// | system page m+k |(b) | | -// | | â–¼ | | -// +-------------------+--- (d) | -// | | â–² | | -// | system page m+k+1 |(a) | | -// | | â–¼ â–¼ | -// +-------------------+----------- | -// | | | -// | ... | | -// | | â–¼ -// +-------------------+--------------- -// -// This means some allocation slots will be reserved to hold PA -// metadata. We exclude these pseudo slots from the GWP-ASan free list so that -// they are never used for anything other that storing the metadata. -class PA_COMPONENT_EXPORT(PARTITION_ALLOC) GwpAsanSupport { - public: - static void* MapRegion(size_t slot_count, std::vector<uint16_t>& free_list); - static bool CanReuse(uintptr_t slot_start); -}; - -} // namespace partition_alloc - -#endif // BUILDFLAG(ENABLE_GWP_ASAN_SUPPORT) - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_GWP_ASAN_SUPPORT_H_
diff --git a/base/allocator/partition_allocator/hardening_unittest.cc b/base/allocator/partition_allocator/hardening_unittest.cc deleted file mode 100644 index b69721c..0000000 --- a/base/allocator/partition_allocator/hardening_unittest.cc +++ /dev/null
@@ -1,169 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <cstdint> -#include <string> -#include <vector> - -#include "base/allocator/partition_allocator/partition_alloc_config.h" -#include "base/allocator/partition_allocator/partition_freelist_entry.h" -#include "base/allocator/partition_allocator/partition_page.h" -#include "base/allocator/partition_allocator/partition_root.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -// With *SAN, PartitionAlloc is rerouted to malloc(). -#if !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - -namespace partition_alloc::internal { -namespace { - -// Death tests misbehave on Android, crbug.com/1240184 -#if !BUILDFLAG(IS_ANDROID) && defined(GTEST_HAS_DEATH_TEST) && \ - PA_CONFIG(HAS_FREELIST_SHADOW_ENTRY) - -TEST(HardeningTest, PartialCorruption) { - std::string important_data("very important"); - char* to_corrupt = const_cast<char*>(important_data.c_str()); - - PartitionRoot<ThreadSafe> root({ - PartitionOptions::AlignedAlloc::kAllowed, - PartitionOptions::ThreadCache::kDisabled, - PartitionOptions::Quarantine::kDisallowed, - PartitionOptions::Cookie::kDisallowed, - PartitionOptions::BackupRefPtr::kDisabled, - PartitionOptions::BackupRefPtrZapping::kDisabled, - PartitionOptions::UseConfigurablePool::kNo, - }); - root.UncapEmptySlotSpanMemoryForTesting(); - - const size_t kAllocSize = 100; - void* data = root.Alloc(kAllocSize, ""); - void* data2 = root.Alloc(kAllocSize, ""); - root.Free(data2); - root.Free(data); - - // root->bucket->active_slot_span_head->freelist_head points to data, next_ - // points to data2. We can corrupt *data to get overwrite the next_ pointer. - // Even if it looks reasonable (valid encoded pointer), freelist corruption - // detection will make the code crash, because shadow_ doesn't match - // encoded_next_. - PartitionFreelistEntry::EmplaceAndInitForTest(root.ObjectToSlotStart(data), - to_corrupt, false); - EXPECT_DEATH(root.Alloc(kAllocSize, ""), ""); -} - -TEST(HardeningTest, OffHeapPointerCrashing) { - std::string important_data("very important"); - char* to_corrupt = const_cast<char*>(important_data.c_str()); - - PartitionRoot<ThreadSafe> root({ - PartitionOptions::AlignedAlloc::kAllowed, - PartitionOptions::ThreadCache::kDisabled, - PartitionOptions::Quarantine::kDisallowed, - PartitionOptions::Cookie::kDisallowed, - PartitionOptions::BackupRefPtr::kDisabled, - PartitionOptions::BackupRefPtrZapping::kDisabled, - PartitionOptions::UseConfigurablePool::kNo, - }); - root.UncapEmptySlotSpanMemoryForTesting(); - - const size_t kAllocSize = 100; - void* data = root.Alloc(kAllocSize, ""); - void* data2 = root.Alloc(kAllocSize, ""); - root.Free(data2); - root.Free(data); - - // See "PartialCorruption" above for details. This time, make shadow_ - // consistent. - PartitionFreelistEntry::EmplaceAndInitForTest(root.ObjectToSlotStart(data), - to_corrupt, true); - - // Crashes, because |to_corrupt| is not on the same superpage as data. - EXPECT_DEATH(root.Alloc(kAllocSize, ""), ""); -} - -TEST(HardeningTest, MetadataPointerCrashing) { - PartitionRoot<ThreadSafe> root({ - PartitionOptions::AlignedAlloc::kAllowed, - PartitionOptions::ThreadCache::kDisabled, - PartitionOptions::Quarantine::kDisallowed, - PartitionOptions::Cookie::kDisallowed, - PartitionOptions::BackupRefPtr::kDisabled, - PartitionOptions::BackupRefPtrZapping::kDisabled, - PartitionOptions::UseConfigurablePool::kNo, - }); - root.UncapEmptySlotSpanMemoryForTesting(); - - const size_t kAllocSize = 100; - void* data = root.Alloc(kAllocSize, ""); - void* data2 = root.Alloc(kAllocSize, ""); - root.Free(data2); - root.Free(data); - - uintptr_t slot_start = root.ObjectToSlotStart(data); - auto* metadata = SlotSpanMetadata<ThreadSafe>::FromSlotStart(slot_start); - PartitionFreelistEntry::EmplaceAndInitForTest(slot_start, metadata, true); - - // Crashes, because |metadata| points inside the metadata area. - EXPECT_DEATH(root.Alloc(kAllocSize, ""), ""); -} -#endif // !BUILDFLAG(IS_ANDROID) && defined(GTEST_HAS_DEATH_TEST) && - // PA_CONFIG(HAS_FREELIST_SHADOW_ENTRY) - -// Below test also misbehaves on Android; as above, death tests don't -// quite work (crbug.com/1240184), and having free slot bitmaps enabled -// force the expectations below to crash. -#if !BUILDFLAG(IS_ANDROID) - -TEST(HardeningTest, SuccessfulCorruption) { - PartitionRoot<ThreadSafe> root({ - PartitionOptions::AlignedAlloc::kAllowed, - PartitionOptions::ThreadCache::kDisabled, - PartitionOptions::Quarantine::kDisallowed, - PartitionOptions::Cookie::kDisallowed, - PartitionOptions::BackupRefPtr::kDisabled, - PartitionOptions::BackupRefPtrZapping::kDisabled, - PartitionOptions::UseConfigurablePool::kNo, - }); - root.UncapEmptySlotSpanMemoryForTesting(); - - uintptr_t* zero_vector = reinterpret_cast<uintptr_t*>( - root.AllocWithFlags(AllocFlags::kZeroFill, 100 * sizeof(uintptr_t), "")); - ASSERT_TRUE(zero_vector); - // Pointer to the middle of an existing allocation. - uintptr_t* to_corrupt = zero_vector + 20; - - const size_t kAllocSize = 100; - void* data = root.Alloc(kAllocSize, ""); - void* data2 = root.Alloc(kAllocSize, ""); - root.Free(data2); - root.Free(data); - - PartitionFreelistEntry::EmplaceAndInitForTest(root.ObjectToSlotStart(data), - to_corrupt, true); - -#if BUILDFLAG(USE_FREESLOT_BITMAP) - // This part crashes with freeslot bitmap because it detects freelist - // corruptions, which is rather desirable behavior. - EXPECT_DEATH_IF_SUPPORTED(root.Alloc(kAllocSize, ""), ""); -#else - // Next allocation is what was in - // root->bucket->active_slot_span_head->freelist_head, so not the corrupted - // pointer. - void* new_data = root.Alloc(kAllocSize, ""); - ASSERT_EQ(new_data, data); - - // Not crashing, because a zeroed area is a "valid" freelist entry. - void* new_data2 = root.Alloc(kAllocSize, ""); - // Now we have a pointer to the middle of an existing allocation. - EXPECT_EQ(new_data2, to_corrupt); -#endif // BUILDFLAG(USE_FREESLOT_BITMAP) -} -#endif // !BUILDFLAG(IS_ANDROID) - -} // namespace -} // namespace partition_alloc::internal - -#endif // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
diff --git a/base/allocator/partition_allocator/memory_reclaimer.cc b/base/allocator/partition_allocator/memory_reclaimer.cc deleted file mode 100644 index dc7b3e4..0000000 --- a/base/allocator/partition_allocator/memory_reclaimer.cc +++ /dev/null
@@ -1,99 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/memory_reclaimer.h" - -#include "base/allocator/partition_allocator/partition_alloc.h" -#include "base/allocator/partition_allocator/partition_alloc_base/no_destructor.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/partition_alloc_config.h" - -#if BUILDFLAG(USE_STARSCAN) -#include "base/allocator/partition_allocator/starscan/pcscan.h" -#endif - -namespace partition_alloc { - -// static -MemoryReclaimer* MemoryReclaimer::Instance() { - static internal::base::NoDestructor<MemoryReclaimer> instance; - return instance.get(); -} - -void MemoryReclaimer::RegisterPartition(PartitionRoot<>* partition) { - internal::ScopedGuard lock(lock_); - PA_DCHECK(partition); - auto it_and_whether_inserted = partitions_.insert(partition); - PA_DCHECK(it_and_whether_inserted.second); -} - -void MemoryReclaimer::UnregisterPartition( - PartitionRoot<internal::ThreadSafe>* partition) { - internal::ScopedGuard lock(lock_); - PA_DCHECK(partition); - size_t erased_count = partitions_.erase(partition); - PA_DCHECK(erased_count == 1u); -} - -MemoryReclaimer::MemoryReclaimer() = default; -MemoryReclaimer::~MemoryReclaimer() = default; - -void MemoryReclaimer::ReclaimAll() { - constexpr int kFlags = PurgeFlags::kDecommitEmptySlotSpans | - PurgeFlags::kDiscardUnusedSystemPages | - PurgeFlags::kAggressiveReclaim; - Reclaim(kFlags); -} - -void MemoryReclaimer::ReclaimNormal() { - constexpr int kFlags = PurgeFlags::kDecommitEmptySlotSpans | - PurgeFlags::kDiscardUnusedSystemPages; - Reclaim(kFlags); -} - -void MemoryReclaimer::Reclaim(int flags) { - internal::ScopedGuard lock( - lock_); // Has to protect from concurrent (Un)Register calls. - - // PCScan quarantines freed slots. Trigger the scan first to let it call - // FreeNoHooksImmediate on slots that pass the quarantine. - // - // In turn, FreeNoHooksImmediate may add slots to thread cache. Purge it next - // so that the slots are actually freed. (This is done synchronously only for - // the current thread.) - // - // Lastly decommit empty slot spans and lastly try to discard unused pages at - // the end of the remaining active slots. -#if PA_CONFIG(STARSCAN_ENABLE_STARSCAN_ON_RECLAIM) && BUILDFLAG(USE_STARSCAN) - { - using PCScan = internal::PCScan; - const auto invocation_mode = flags & PurgeFlags::kAggressiveReclaim - ? PCScan::InvocationMode::kForcedBlocking - : PCScan::InvocationMode::kBlocking; - PCScan::PerformScanIfNeeded(invocation_mode); - } -#endif // PA_CONFIG(STARSCAN_ENABLE_STARSCAN_ON_RECLAIM) && - // BUILDFLAG(USE_STARSCAN) - -#if PA_CONFIG(THREAD_CACHE_SUPPORTED) - // Don't completely empty the thread cache outside of low memory situations, - // as there is periodic purge which makes sure that it doesn't take too much - // space. - if (flags & PurgeFlags::kAggressiveReclaim) { - ThreadCacheRegistry::Instance().PurgeAll(); - } -#endif // PA_CONFIG(THREAD_CACHE_SUPPORTED) - - for (auto* partition : partitions_) { - partition->PurgeMemory(flags); - } -} - -void MemoryReclaimer::ResetForTesting() { - internal::ScopedGuard lock(lock_); - partitions_.clear(); -} - -} // namespace partition_alloc
diff --git a/base/allocator/partition_allocator/memory_reclaimer.h b/base/allocator/partition_allocator/memory_reclaimer.h deleted file mode 100644 index 8a4c2f2..0000000 --- a/base/allocator/partition_allocator/memory_reclaimer.h +++ /dev/null
@@ -1,72 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_MEMORY_RECLAIMER_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_MEMORY_RECLAIMER_H_ - -#include <memory> -#include <set> - -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_base/no_destructor.h" -#include "base/allocator/partition_allocator/partition_alloc_base/thread_annotations.h" -#include "base/allocator/partition_allocator/partition_alloc_base/time/time.h" -#include "base/allocator/partition_allocator/partition_alloc_forward.h" -#include "base/allocator/partition_allocator/partition_lock.h" - -namespace partition_alloc { - -// Posts and handles memory reclaim tasks for PartitionAlloc. -// -// PartitionAlloc users are responsible for scheduling and calling the -// reclamation methods with their own timers / event loops. -// -// Singleton as this runs as long as the process is alive, and -// having multiple instances would be wasteful. -class PA_COMPONENT_EXPORT(PARTITION_ALLOC) MemoryReclaimer { - public: - static MemoryReclaimer* Instance(); - - MemoryReclaimer(const MemoryReclaimer&) = delete; - MemoryReclaimer& operator=(const MemoryReclaimer&) = delete; - - // Internal. Do not use. - // Registers a partition to be tracked by the reclaimer. - void RegisterPartition(PartitionRoot<>* partition); - // Internal. Do not use. - // Unregisters a partition to be tracked by the reclaimer. - void UnregisterPartition(PartitionRoot<>* partition); - - // Triggers an explicit reclaim now to reclaim as much free memory as - // possible. The API callers need to invoke this method periodically - // if they want to use memory reclaimer. - // See also GetRecommendedReclaimIntervalInMicroseconds()'s comment. - void ReclaimNormal(); - - // Returns a recommended interval to invoke ReclaimNormal. - int64_t GetRecommendedReclaimIntervalInMicroseconds() { - return internal::base::Seconds(4).InMicroseconds(); - } - - // Triggers an explicit reclaim now reclaiming all free memory - void ReclaimAll(); - - private: - MemoryReclaimer(); - ~MemoryReclaimer(); - // |flags| is an OR of base::PartitionPurgeFlags - void Reclaim(int flags); - void ReclaimAndReschedule(); - void ResetForTesting(); - - internal::Lock lock_; - std::set<PartitionRoot<>*> partitions_ PA_GUARDED_BY(lock_); - - friend class internal::base::NoDestructor<MemoryReclaimer>; - friend class MemoryReclaimerTest; -}; - -} // namespace partition_alloc - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_MEMORY_RECLAIMER_H_
diff --git a/base/allocator/partition_allocator/memory_reclaimer_unittest.cc b/base/allocator/partition_allocator/memory_reclaimer_unittest.cc deleted file mode 100644 index 8c04f16..0000000 --- a/base/allocator/partition_allocator/memory_reclaimer_unittest.cc +++ /dev/null
@@ -1,157 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/memory_reclaimer.h" - -#include <memory> -#include <utility> - -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/logging.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_config.h" -#include "base/allocator/partition_allocator/partition_alloc_for_testing.h" -#include "base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_partition_alloc.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \ - PA_CONFIG(THREAD_CACHE_SUPPORTED) -#include "base/allocator/partition_allocator/extended_api.h" -#include "base/allocator/partition_allocator/thread_cache.h" -#endif - -// Otherwise, PartitionAlloc doesn't allocate any memory, and the tests are -// meaningless. -#if !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - -namespace partition_alloc { - -namespace { - -void HandleOOM(size_t unused_size) { - PA_LOG(FATAL) << "Out of memory"; -} - -} // namespace - -class MemoryReclaimerTest : public ::testing::Test { - public: - MemoryReclaimerTest() { - // Since MemoryReclaimer::ResetForTesting() clears partitions_, - // we need to make PartitionAllocator after this ResetForTesting(). - // Otherwise, we will see no PartitionAllocator is registered. - MemoryReclaimer::Instance()->ResetForTesting(); - - allocator_ = - std::make_unique<PartitionAllocatorForTesting>(PartitionOptions{ - PartitionOptions::AlignedAlloc::kDisallowed, - PartitionOptions::ThreadCache::kDisabled, - PartitionOptions::Quarantine::kAllowed, - PartitionOptions::Cookie::kAllowed, - PartitionOptions::BackupRefPtr::kDisabled, - PartitionOptions::BackupRefPtrZapping::kDisabled, - PartitionOptions::UseConfigurablePool::kNo, - }); - allocator_->root()->UncapEmptySlotSpanMemoryForTesting(); - PartitionAllocGlobalInit(HandleOOM); - } - - ~MemoryReclaimerTest() override { - // Since MemoryReclaimer::UnregisterPartition() checks whether - // the given partition is managed by MemoryReclaimer, need to - // destruct |allocator_| before ResetForTesting(). - allocator_ = nullptr; - PartitionAllocGlobalUninitForTesting(); - } - - void Reclaim() { MemoryReclaimer::Instance()->ReclaimNormal(); } - - void AllocateAndFree() { - void* data = allocator_->root()->Alloc(1, ""); - allocator_->root()->Free(data); - } - - std::unique_ptr<PartitionAllocatorForTesting> allocator_; -}; - -TEST_F(MemoryReclaimerTest, FreesMemory) { - PartitionRoot<internal::ThreadSafe>* root = allocator_->root(); - - size_t committed_initially = root->get_total_size_of_committed_pages(); - AllocateAndFree(); - size_t committed_before = root->get_total_size_of_committed_pages(); - - EXPECT_GT(committed_before, committed_initially); - - Reclaim(); - size_t committed_after = root->get_total_size_of_committed_pages(); - EXPECT_LT(committed_after, committed_before); - EXPECT_LE(committed_initially, committed_after); -} - -TEST_F(MemoryReclaimerTest, Reclaim) { - PartitionRoot<internal::ThreadSafe>* root = allocator_->root(); - size_t committed_initially = root->get_total_size_of_committed_pages(); - - { - AllocateAndFree(); - - size_t committed_before = root->get_total_size_of_committed_pages(); - EXPECT_GT(committed_before, committed_initially); - MemoryReclaimer::Instance()->ReclaimAll(); - size_t committed_after = root->get_total_size_of_committed_pages(); - - EXPECT_LT(committed_after, committed_before); - EXPECT_LE(committed_initially, committed_after); - } -} - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \ - PA_CONFIG(THREAD_CACHE_SUPPORTED) - -namespace { -// malloc() / free() pairs can be removed by the compiler, this is enough (for -// now) to prevent that. -PA_NOINLINE void FreeForTest(void* data) { - free(data); -} -} // namespace - -TEST_F(MemoryReclaimerTest, DoNotAlwaysPurgeThreadCache) { - // Make sure the thread cache is enabled in the main partition. - internal::ThreadCacheProcessScopeForTesting scope( - allocator_shim::internal::PartitionAllocMalloc::Allocator()); - - for (size_t i = 0; i < ThreadCache::kDefaultSizeThreshold; i++) { - void* data = malloc(i); - FreeForTest(data); - } - - auto* tcache = ThreadCache::Get(); - ASSERT_TRUE(tcache); - size_t cached_size = tcache->CachedMemory(); - - Reclaim(); - - // No thread cache purging during periodic purge, but with ReclaimAll(). - // - // Cannot assert on the exact size of the thread cache, since it can shrink - // when a buffer is overfull, and this may happen through other malloc() - // allocations in the test harness. - EXPECT_GT(tcache->CachedMemory(), cached_size / 2); - - Reclaim(); - EXPECT_GT(tcache->CachedMemory(), cached_size / 2); - - MemoryReclaimer::Instance()->ReclaimAll(); - EXPECT_LT(tcache->CachedMemory(), cached_size / 2); -} - -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \ - // PA_CONFIG(THREAD_CACHE_SUPPORTED) - -} // namespace partition_alloc - -#endif // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
diff --git a/base/allocator/partition_allocator/oom.cc b/base/allocator/partition_allocator/oom.cc deleted file mode 100644 index e007390..0000000 --- a/base/allocator/partition_allocator/oom.cc +++ /dev/null
@@ -1,81 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/oom.h" - -#include "base/allocator/partition_allocator/oom_callback.h" -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/alias.h" -#include "base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h" -#include "build/build_config.h" - -#if BUILDFLAG(IS_WIN) -#include <windows.h> - -#include <stdlib.h> - -#include <array> -#endif // BUILDFLAG(IS_WIN) - -namespace partition_alloc { - -size_t g_oom_size = 0U; - -namespace internal { - -// Crash server classifies base::internal::OnNoMemoryInternal as OOM. -// TODO(crbug.com/1151236): Update to -// partition_alloc::internal::base::internal::OnNoMemoryInternal -PA_NOINLINE void OnNoMemoryInternal(size_t size) { - g_oom_size = size; -#if BUILDFLAG(IS_WIN) - // Kill the process. This is important for security since most of code - // does not check the result of memory allocation. - // https://msdn.microsoft.com/en-us/library/het71c37.aspx - // Pass the size of the failed request in an exception argument. - ULONG_PTR exception_args[] = {size}; - ::RaiseException(win::kOomExceptionCode, EXCEPTION_NONCONTINUABLE, - std::size(exception_args), exception_args); - - // Safety check, make sure process exits here. - _exit(win::kOomExceptionCode); -#else - size_t tmp_size = size; - internal::base::debug::Alias(&tmp_size); - - // Note: Don't add anything that may allocate here. Depending on the - // allocator, this may be called from within the allocator (e.g. with - // PartitionAlloc), and would deadlock as our locks are not recursive. - // - // Additionally, this is unlikely to work, since allocating from an OOM - // handler is likely to fail. - // - // Use PA_IMMEDIATE_CRASH() so that the top frame in the crash is our code, - // rather than using abort() or similar; this avoids the crash server needing - // to be able to successfully unwind through libc to get to the correct - // address, which is particularly an issue on Android. - PA_IMMEDIATE_CRASH(); -#endif // BUILDFLAG(IS_WIN) -} - -} // namespace internal - -void TerminateBecauseOutOfMemory(size_t size) { - internal::OnNoMemoryInternal(size); -} - -namespace internal { - -// The crash is generated in a PA_NOINLINE function so that we can classify the -// crash as an OOM solely by analyzing the stack trace. It is tagged as -// PA_NOT_TAIL_CALLED to ensure that its parent function stays on the stack. -[[noreturn]] PA_NOINLINE PA_NOT_TAIL_CALLED void OnNoMemory(size_t size) { - RunPartitionAllocOomCallback(); - TerminateBecauseOutOfMemory(size); - PA_IMMEDIATE_CRASH(); -} - -} // namespace internal - -} // namespace partition_alloc
diff --git a/base/allocator/partition_allocator/oom.h b/base/allocator/partition_allocator/oom.h deleted file mode 100644 index 493dff9..0000000 --- a/base/allocator/partition_allocator/oom.h +++ /dev/null
@@ -1,70 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_OOM_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_OOM_H_ - -#include <cstddef> - -#include "base/allocator/partition_allocator/allocation_guard.h" -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "build/build_config.h" - -#if BUILDFLAG(IS_WIN) -#include "base/allocator/partition_allocator/partition_alloc_base/win/windows_types.h" -#endif - -namespace partition_alloc { - -// Terminates process. Should be called only for out of memory errors. -// |size| is the size of the failed allocation, or 0 if not known. -// Crash reporting classifies such crashes as OOM. -// Must be allocation-safe. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -void TerminateBecauseOutOfMemory(size_t size); - -// Records the size of the allocation that caused the current OOM crash, for -// consumption by Breakpad. -// TODO: this can be removed when Breakpad is no longer supported. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) extern size_t g_oom_size; - -#if BUILDFLAG(IS_WIN) -namespace win { - -// Custom Windows exception code chosen to indicate an out of memory error. -// See https://msdn.microsoft.com/en-us/library/het71c37.aspx. -// "To make sure that you do not define a code that conflicts with an existing -// exception code" ... "The resulting error code should therefore have the -// highest four bits set to hexadecimal E." -// 0xe0000008 was chosen arbitrarily, as 0x00000008 is ERROR_NOT_ENOUGH_MEMORY. -const DWORD kOomExceptionCode = 0xe0000008; - -} // namespace win -#endif - -namespace internal { - -// The crash is generated in a PA_NOINLINE function so that we can classify the -// crash as an OOM solely by analyzing the stack trace. It is tagged as -// PA_NOT_TAIL_CALLED to ensure that its parent function stays on the stack. -[[noreturn]] PA_NOT_TAIL_CALLED PA_COMPONENT_EXPORT( - PARTITION_ALLOC) void OnNoMemory(size_t size); - -// OOM_CRASH(size) - Specialization of IMMEDIATE_CRASH which will raise a custom -// exception on Windows to signal this is OOM and not a normal assert. -// OOM_CRASH(size) is called by users of PageAllocator (including -// PartitionAlloc) to signify an allocation failure from the platform. -#define OOM_CRASH(size) \ - do { \ - /* Raising an exception might allocate, allow that. */ \ - ::partition_alloc::ScopedAllowAllocations guard{}; \ - ::partition_alloc::internal::OnNoMemory(size); \ - } while (0) - -} // namespace internal - -} // namespace partition_alloc - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_OOM_H_
diff --git a/base/allocator/partition_allocator/oom_callback.cc b/base/allocator/partition_allocator/oom_callback.cc deleted file mode 100644 index 75877ea..0000000 --- a/base/allocator/partition_allocator/oom_callback.cc +++ /dev/null
@@ -1,28 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/oom_callback.h" - -#include "base/allocator/partition_allocator/partition_alloc_check.h" - -namespace partition_alloc { - -namespace { -PartitionAllocOomCallback g_oom_callback; -} // namespace - -void SetPartitionAllocOomCallback(PartitionAllocOomCallback callback) { - PA_DCHECK(!g_oom_callback); - g_oom_callback = callback; -} - -namespace internal { -void RunPartitionAllocOomCallback() { - if (g_oom_callback) { - g_oom_callback(); - } -} -} // namespace internal - -} // namespace partition_alloc
diff --git a/base/allocator/partition_allocator/oom_callback.h b/base/allocator/partition_allocator/oom_callback.h deleted file mode 100644 index f3730ec..0000000 --- a/base/allocator/partition_allocator/oom_callback.h +++ /dev/null
@@ -1,26 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_OOM_CALLBACK_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_OOM_CALLBACK_H_ - -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" - -namespace partition_alloc { - -using PartitionAllocOomCallback = void (*)(); - -// Registers a callback to be invoked during an OOM_CRASH(). OOM_CRASH is -// invoked by users of PageAllocator (including PartitionAlloc) to signify an -// allocation failure from the platform. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -void SetPartitionAllocOomCallback(PartitionAllocOomCallback callback); - -namespace internal { -PA_COMPONENT_EXPORT(PARTITION_ALLOC) void RunPartitionAllocOomCallback(); -} // namespace internal - -} // namespace partition_alloc - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_OOM_CALLBACK_H_
diff --git a/base/allocator/partition_allocator/page_allocator.cc b/base/allocator/partition_allocator/page_allocator.cc deleted file mode 100644 index aaa6906..0000000 --- a/base/allocator/partition_allocator/page_allocator.cc +++ /dev/null
@@ -1,412 +0,0 @@ -// Copyright 2013 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/page_allocator.h" - -#include <atomic> -#include <cstdint> - -#include "base/allocator/partition_allocator/address_space_randomization.h" -#include "base/allocator/partition_allocator/page_allocator_internal.h" -#include "base/allocator/partition_allocator/partition_alloc_base/bits.h" -#include "base/allocator/partition_allocator/partition_alloc_base/thread_annotations.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/partition_lock.h" -#include "build/build_config.h" - -#if BUILDFLAG(IS_WIN) -#include <windows.h> -#endif - -#if BUILDFLAG(IS_WIN) -#include "base/allocator/partition_allocator/page_allocator_internals_win.h" -#elif BUILDFLAG(IS_POSIX) -#include "base/allocator/partition_allocator/page_allocator_internals_posix.h" -#elif BUILDFLAG(IS_FUCHSIA) -#include "base/allocator/partition_allocator/page_allocator_internals_fuchsia.h" -#else -#error Platform not supported. -#endif - -namespace partition_alloc { - -namespace { - -internal::Lock g_reserve_lock; - -// We may reserve/release address space on different threads. -internal::Lock& GetReserveLock() { - return g_reserve_lock; -} - -std::atomic<size_t> g_total_mapped_address_space; - -// We only support a single block of reserved address space. -uintptr_t s_reservation_address PA_GUARDED_BY(GetReserveLock()) = 0; -size_t s_reservation_size PA_GUARDED_BY(GetReserveLock()) = 0; - -uintptr_t AllocPagesIncludingReserved( - uintptr_t address, - size_t length, - PageAccessibilityConfiguration accessibility, - PageTag page_tag, - int file_descriptor_for_shared_alloc = -1) { - uintptr_t ret = - internal::SystemAllocPages(address, length, accessibility, page_tag, - file_descriptor_for_shared_alloc); - if (!ret) { - const bool cant_alloc_length = internal::kHintIsAdvisory || !address; - if (cant_alloc_length) { - // The system cannot allocate |length| bytes. Release any reserved address - // space and try once more. - ReleaseReservation(); - ret = internal::SystemAllocPages(address, length, accessibility, page_tag, - file_descriptor_for_shared_alloc); - } - } - return ret; -} - -// Trims memory at |base_address| to given |trim_length| and |alignment|. -// -// On failure, on Windows, this function returns 0 and frees memory at -// |base_address|. -uintptr_t TrimMapping(uintptr_t base_address, - size_t base_length, - size_t trim_length, - uintptr_t alignment, - uintptr_t alignment_offset, - PageAccessibilityConfiguration accessibility) { - PA_DCHECK(base_length >= trim_length); - PA_DCHECK(internal::base::bits::IsPowerOfTwo(alignment)); - PA_DCHECK(alignment_offset < alignment); - uintptr_t new_base = - NextAlignedWithOffset(base_address, alignment, alignment_offset); - PA_DCHECK(new_base >= base_address); - size_t pre_slack = new_base - base_address; - size_t post_slack = base_length - pre_slack - trim_length; - PA_DCHECK(base_length == trim_length || pre_slack || post_slack); - PA_DCHECK(pre_slack < base_length); - PA_DCHECK(post_slack < base_length); - return internal::TrimMappingInternal(base_address, base_length, trim_length, - accessibility, pre_slack, post_slack); -} - -} // namespace - -// Align |address| up to the closest, non-smaller address, that gives -// |requested_offset| remainder modulo |alignment|. -// -// Examples for alignment=1024 and requested_offset=64: -// 64 -> 64 -// 65 -> 1088 -// 1024 -> 1088 -// 1088 -> 1088 -// 1089 -> 2112 -// 2048 -> 2112 -uintptr_t NextAlignedWithOffset(uintptr_t address, - uintptr_t alignment, - uintptr_t requested_offset) { - PA_DCHECK(internal::base::bits::IsPowerOfTwo(alignment)); - PA_DCHECK(requested_offset < alignment); - - uintptr_t actual_offset = address & (alignment - 1); - uintptr_t new_address; - if (actual_offset <= requested_offset) { - new_address = address + requested_offset - actual_offset; - } else { - new_address = address + alignment + requested_offset - actual_offset; - } - PA_DCHECK(new_address >= address); - PA_DCHECK(new_address - address < alignment); - PA_DCHECK(new_address % alignment == requested_offset); - - return new_address; -} - -namespace internal { - -uintptr_t SystemAllocPages(uintptr_t hint, - size_t length, - PageAccessibilityConfiguration accessibility, - PageTag page_tag, - int file_descriptor_for_shared_alloc) { - PA_DCHECK(!(length & internal::PageAllocationGranularityOffsetMask())); - PA_DCHECK(!(hint & internal::PageAllocationGranularityOffsetMask())); - uintptr_t ret = internal::SystemAllocPagesInternal( - hint, length, accessibility, page_tag, file_descriptor_for_shared_alloc); - if (ret) { - g_total_mapped_address_space.fetch_add(length, std::memory_order_relaxed); - } - - return ret; -} - -} // namespace internal - -uintptr_t AllocPages(size_t length, - size_t align, - PageAccessibilityConfiguration accessibility, - PageTag page_tag, - int file_descriptor_for_shared_alloc) { - return AllocPagesWithAlignOffset(0, length, align, 0, accessibility, page_tag, - file_descriptor_for_shared_alloc); -} -uintptr_t AllocPages(uintptr_t address, - size_t length, - size_t align, - PageAccessibilityConfiguration accessibility, - PageTag page_tag) { - return AllocPagesWithAlignOffset(address, length, align, 0, accessibility, - page_tag); -} -void* AllocPages(void* address, - size_t length, - size_t align, - PageAccessibilityConfiguration accessibility, - PageTag page_tag) { - return reinterpret_cast<void*>( - AllocPages(reinterpret_cast<uintptr_t>(address), length, align, - accessibility, page_tag)); -} - -uintptr_t AllocPagesWithAlignOffset( - uintptr_t address, - size_t length, - size_t align, - size_t align_offset, - PageAccessibilityConfiguration accessibility, - PageTag page_tag, - int file_descriptor_for_shared_alloc) { - PA_DCHECK(length >= internal::PageAllocationGranularity()); - PA_DCHECK(!(length & internal::PageAllocationGranularityOffsetMask())); - PA_DCHECK(align >= internal::PageAllocationGranularity()); - // Alignment must be power of 2 for masking math to work. - PA_DCHECK(internal::base::bits::IsPowerOfTwo(align)); - PA_DCHECK(align_offset < align); - PA_DCHECK(!(align_offset & internal::PageAllocationGranularityOffsetMask())); - PA_DCHECK(!(address & internal::PageAllocationGranularityOffsetMask())); - uintptr_t align_offset_mask = align - 1; - uintptr_t align_base_mask = ~align_offset_mask; - PA_DCHECK(!address || (address & align_offset_mask) == align_offset); - - // If the client passed null as the address, choose a good one. - if (!address) { - address = (GetRandomPageBase() & align_base_mask) + align_offset; - } - - // First try to force an exact-size, aligned allocation from our random base. -#if defined(ARCH_CPU_32_BITS) - // On 32 bit systems, first try one random aligned address, and then try an - // aligned address derived from the value of |ret|. - constexpr int kExactSizeTries = 2; -#else - // On 64 bit systems, try 3 random aligned addresses. - constexpr int kExactSizeTries = 3; -#endif - - for (int i = 0; i < kExactSizeTries; ++i) { - uintptr_t ret = - AllocPagesIncludingReserved(address, length, accessibility, page_tag, - file_descriptor_for_shared_alloc); - if (ret) { - // If the alignment is to our liking, we're done. - if ((ret & align_offset_mask) == align_offset) { - return ret; - } - // Free the memory and try again. - FreePages(ret, length); - } else { - // |ret| is null; if this try was unhinted, we're OOM. - if (internal::kHintIsAdvisory || !address) { - return 0; - } - } - -#if defined(ARCH_CPU_32_BITS) - // For small address spaces, try the first aligned address >= |ret|. Note - // |ret| may be null, in which case |address| becomes null. If - // |align_offset| is non-zero, this calculation may get us not the first, - // but the next matching address. - address = ((ret + align_offset_mask) & align_base_mask) + align_offset; -#else // defined(ARCH_CPU_64_BITS) - // Keep trying random addresses on systems that have a large address space. - address = NextAlignedWithOffset(GetRandomPageBase(), align, align_offset); -#endif - } - - // Make a larger allocation so we can force alignment. - size_t try_length = length + (align - internal::PageAllocationGranularity()); - PA_CHECK(try_length >= length); - uintptr_t ret; - - do { - // Continue randomizing only on POSIX. - address = internal::kHintIsAdvisory ? GetRandomPageBase() : 0; - ret = - AllocPagesIncludingReserved(address, try_length, accessibility, - page_tag, file_descriptor_for_shared_alloc); - // The retries are for Windows, where a race can steal our mapping on - // resize. - } while (ret && (ret = TrimMapping(ret, try_length, length, align, - align_offset, accessibility)) == 0); - - return ret; -} - -void FreePages(uintptr_t address, size_t length) { - PA_DCHECK(!(address & internal::PageAllocationGranularityOffsetMask())); - PA_DCHECK(!(length & internal::PageAllocationGranularityOffsetMask())); - internal::FreePagesInternal(address, length); - PA_DCHECK(g_total_mapped_address_space.load(std::memory_order_relaxed) > 0); - g_total_mapped_address_space.fetch_sub(length, std::memory_order_relaxed); -} -void FreePages(void* address, size_t length) { - FreePages(reinterpret_cast<uintptr_t>(address), length); -} - -bool TrySetSystemPagesAccess(uintptr_t address, - size_t length, - PageAccessibilityConfiguration accessibility) { - PA_DCHECK(!(length & internal::SystemPageOffsetMask())); - return internal::TrySetSystemPagesAccessInternal(address, length, - accessibility); -} -bool TrySetSystemPagesAccess(void* address, - size_t length, - PageAccessibilityConfiguration accessibility) { - return TrySetSystemPagesAccess(reinterpret_cast<uintptr_t>(address), length, - accessibility); -} - -void SetSystemPagesAccess(uintptr_t address, - size_t length, - PageAccessibilityConfiguration accessibility) { - PA_DCHECK(!(length & internal::SystemPageOffsetMask())); - internal::SetSystemPagesAccessInternal(address, length, accessibility); -} - -void DecommitSystemPages( - uintptr_t address, - size_t length, - PageAccessibilityDisposition accessibility_disposition) { - PA_DCHECK(!(address & internal::SystemPageOffsetMask())); - PA_DCHECK(!(length & internal::SystemPageOffsetMask())); - internal::DecommitSystemPagesInternal(address, length, - accessibility_disposition); -} -void DecommitSystemPages( - void* address, - size_t length, - PageAccessibilityDisposition accessibility_disposition) { - DecommitSystemPages(reinterpret_cast<uintptr_t>(address), length, - accessibility_disposition); -} - -void DecommitAndZeroSystemPages(uintptr_t address, size_t length) { - PA_DCHECK(!(address & internal::SystemPageOffsetMask())); - PA_DCHECK(!(length & internal::SystemPageOffsetMask())); - internal::DecommitAndZeroSystemPagesInternal(address, length); -} -void DecommitAndZeroSystemPages(void* address, size_t length) { - DecommitAndZeroSystemPages(reinterpret_cast<uintptr_t>(address), length); -} - -void RecommitSystemPages( - uintptr_t address, - size_t length, - PageAccessibilityConfiguration accessibility, - PageAccessibilityDisposition accessibility_disposition) { - PA_DCHECK(!(address & internal::SystemPageOffsetMask())); - PA_DCHECK(!(length & internal::SystemPageOffsetMask())); - PA_DCHECK(accessibility.permissions != - PageAccessibilityConfiguration::kInaccessible); - internal::RecommitSystemPagesInternal(address, length, accessibility, - accessibility_disposition); -} - -bool TryRecommitSystemPages( - uintptr_t address, - size_t length, - PageAccessibilityConfiguration accessibility, - PageAccessibilityDisposition accessibility_disposition) { - // Duplicated because we want errors to be reported at a lower level in the - // crashing case. - PA_DCHECK(!(address & internal::SystemPageOffsetMask())); - PA_DCHECK(!(length & internal::SystemPageOffsetMask())); - PA_DCHECK(accessibility.permissions != - PageAccessibilityConfiguration::kInaccessible); - return internal::TryRecommitSystemPagesInternal( - address, length, accessibility, accessibility_disposition); -} - -void DiscardSystemPages(uintptr_t address, size_t length) { - PA_DCHECK(!(length & internal::SystemPageOffsetMask())); - internal::DiscardSystemPagesInternal(address, length); -} -void DiscardSystemPages(void* address, size_t length) { - DiscardSystemPages(reinterpret_cast<uintptr_t>(address), length); -} - -bool ReserveAddressSpace(size_t size) { - // To avoid deadlock, call only SystemAllocPages. - internal::ScopedGuard guard(GetReserveLock()); - if (!s_reservation_address) { - uintptr_t mem = internal::SystemAllocPages( - 0, size, - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kInaccessible), - PageTag::kChromium); - if (mem) { - // We guarantee this alignment when reserving address space. - PA_DCHECK(!(mem & internal::PageAllocationGranularityOffsetMask())); - s_reservation_address = mem; - s_reservation_size = size; - return true; - } - } - return false; -} - -bool ReleaseReservation() { - // To avoid deadlock, call only FreePages. - internal::ScopedGuard guard(GetReserveLock()); - if (!s_reservation_address) { - return false; - } - - FreePages(s_reservation_address, s_reservation_size); - s_reservation_address = 0; - s_reservation_size = 0; - return true; -} - -bool HasReservationForTesting() { - internal::ScopedGuard guard(GetReserveLock()); - return s_reservation_address; -} - -uint32_t GetAllocPageErrorCode() { - return internal::s_allocPageErrorCode; -} - -size_t GetTotalMappedSize() { - return g_total_mapped_address_space; -} - -#if BUILDFLAG(IS_WIN) -namespace { -bool g_retry_on_commit_failure = false; -} - -void SetRetryOnCommitFailure(bool retry_on_commit_failure) { - g_retry_on_commit_failure = retry_on_commit_failure; -} - -bool GetRetryOnCommitFailure() { - return g_retry_on_commit_failure; -} -#endif - -} // namespace partition_alloc
diff --git a/base/allocator/partition_allocator/page_allocator.h b/base/allocator/partition_allocator/page_allocator.h deleted file mode 100644 index e134744..0000000 --- a/base/allocator/partition_allocator/page_allocator.h +++ /dev/null
@@ -1,373 +0,0 @@ -// Copyright 2013 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_H_ - -#include <cstddef> -#include <cstdint> - -#include "base/allocator/partition_allocator/page_allocator_constants.h" -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "build/build_config.h" - -namespace partition_alloc { - -struct PageAccessibilityConfiguration { - enum Permissions { - kInaccessible, - kRead, - kReadWrite, - // This flag is mapped to kReadWrite on systems that - // don't support MTE. - kReadWriteTagged, - // This flag is mapped to kReadExecute on systems - // that don't support Arm's BTI. - kReadExecuteProtected, - kReadExecute, - // This flag is deprecated and will go away soon. - // TODO(bbudge) Remove this as soon as V8 doesn't need RWX pages. - kReadWriteExecute, - }; - -#if BUILDFLAG(ENABLE_PKEYS) - constexpr explicit PageAccessibilityConfiguration(Permissions permissions) - : permissions(permissions), pkey(0) {} - constexpr PageAccessibilityConfiguration(Permissions permissions, int pkey) - : permissions(permissions), pkey(pkey) {} -#else - constexpr explicit PageAccessibilityConfiguration(Permissions permissions) - : permissions(permissions) {} -#endif // BUILDFLAG(ENABLE_PKEYS) - - Permissions permissions; -#if BUILDFLAG(ENABLE_PKEYS) - // Tag the page with a Memory Protection Key. Use 0 for none. - int pkey; -#endif // BUILDFLAG(ENABLE_PKEYS) -}; - -// Use for De/RecommitSystemPages API. -enum class PageAccessibilityDisposition { - // Enforces permission update (Decommit will set to - // PageAccessibilityConfiguration::kInaccessible; - // Recommit will set to whatever was requested, other than - // PageAccessibilityConfiguration::kInaccessible). - kRequireUpdate, - // Will not update permissions, if the platform supports that (POSIX & Fuchsia - // only). - kAllowKeepForPerf, -}; - -// macOS supports tagged memory regions, to help in debugging. On Android, -// these tags are used to name anonymous mappings. -enum class PageTag { - kFirst = 240, // Minimum tag value. - kSimulation = 251, // Memory simulator tool. - kBlinkGC = 252, // Blink GC pages. - kPartitionAlloc = 253, // PartitionAlloc, no matter the partition. - kChromium = 254, // Chromium page. - kV8 = 255, // V8 heap pages. - kLast = kV8 // Maximum tag value. -}; - -// See -// https://github.com/apple-oss-distributions/xnu/blob/5c2921b07a2480ab43ec66f5b9e41cb872bc554f/osfmk/mach/vm_statistics.h#L687 -static_assert( - static_cast<int>(PageTag::kLast) < 256, - "Tags are only 1 byte long on macOS, see vm_statistics.h in XNU."); - -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -uintptr_t NextAlignedWithOffset(uintptr_t ptr, - uintptr_t alignment, - uintptr_t requested_offset); - -// Allocates one or more pages. -// -// The requested |address| is just a hint; the actual address returned may -// differ. The returned address will be aligned to |align_offset| modulo |align| -// bytes. -// -// |length|, |align| and |align_offset| are in bytes, and must be a multiple of -// |PageAllocationGranularity()|. |length| and |align| must be non-zero. -// |align_offset| must be less than |align|. |align| must be a power of two. -// -// If |address| is 0/nullptr, then a suitable and randomized address will be -// chosen automatically. -// -// |accessibility| controls the permission of the allocated pages. -// PageAccessibilityConfiguration::kInaccessible means uncommitted. -// -// |page_tag| is used on some platforms to identify the source of the -// allocation. Use PageTag::kChromium as a catch-all category. -// -// |file_descriptor_for_shared_alloc| is only used in mapping the shadow -// pools to the same physical address as the real one in -// PartitionAddressSpace::Init(). It should be ignored in other cases. -// -// This call will return 0/nullptr if the allocation cannot be satisfied. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -uintptr_t AllocPages(size_t length, - size_t align, - PageAccessibilityConfiguration accessibility, - PageTag page_tag, - int file_descriptor_for_shared_alloc = -1); -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -uintptr_t AllocPages(uintptr_t address, - size_t length, - size_t align, - PageAccessibilityConfiguration accessibility, - PageTag page_tag); -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -void* AllocPages(void* address, - size_t length, - size_t align, - PageAccessibilityConfiguration accessibility, - PageTag page_tag); -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -uintptr_t AllocPagesWithAlignOffset( - uintptr_t address, - size_t length, - size_t align, - size_t align_offset, - PageAccessibilityConfiguration page_accessibility, - PageTag page_tag, - int file_descriptor_for_shared_alloc = -1); - -// Frees one or more pages starting at |address| and continuing for |length| -// bytes. -// -// |address| and |length| must match a previous call to |AllocPages|. Therefore, -// |address| must be aligned to |PageAllocationGranularity()| bytes, and -// |length| must be a multiple of |PageAllocationGranularity()|. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -void FreePages(uintptr_t address, size_t length); -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -void FreePages(void* address, size_t length); - -// Marks one or more system pages, starting at |address| with the given -// |page_accessibility|. |length| must be a multiple of |SystemPageSize()| -// bytes. -// -// Returns true if the permission change succeeded. In most cases you must -// |CHECK| the result. -[[nodiscard]] PA_COMPONENT_EXPORT(PARTITION_ALLOC) bool TrySetSystemPagesAccess( - uintptr_t address, - size_t length, - PageAccessibilityConfiguration page_accessibility); -[[nodiscard]] PA_COMPONENT_EXPORT(PARTITION_ALLOC) bool TrySetSystemPagesAccess( - void* address, - size_t length, - PageAccessibilityConfiguration page_accessibility); - -// Marks one or more system pages, starting at |address| with the given -// |page_accessibility|. |length| must be a multiple of |SystemPageSize()| -// bytes. -// -// Performs a CHECK that the operation succeeds. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -void SetSystemPagesAccess(uintptr_t address, - size_t length, - PageAccessibilityConfiguration page_accessibility); -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -void SetSystemPagesAccess(void* address, - size_t length, - PageAccessibilityConfiguration page_accessibility); - -// Decommits one or more system pages starting at |address| and continuing for -// |length| bytes. |address| and |length| must be aligned to a system page -// boundary. -// -// This API will crash if the operation cannot be performed! -// -// If disposition is PageAccessibilityDisposition::kRequireUpdate (recommended), -// the decommitted pages will be made inaccessible before the call returns. -// While it is always a programming error to access decommitted pages without -// first recommitting them, callers may use -// PageAccessibilityDisposition::kAllowKeepForPerf to allow the implementation -// to skip changing permissions (use with care), for performance reasons (see -// crrev.com/c/2567282 and crrev.com/c/2563038 for perf regressions encountered -// in the past). Implementations may choose to always modify permissions, hence -// accessing those pages may or may not trigger a fault. -// -// Decommitting means that physical resources (RAM or swap/pagefile) backing the -// allocated virtual address range may be released back to the system, but the -// address space is still allocated to the process (possibly using up page table -// entries or other accounting resources). There is no guarantee that the pages -// are zeroed, unless |DecommittedMemoryIsAlwaysZeroed()| is true. -// -// This operation may not be atomic on some platforms. -// -// Note: "Committed memory" is a Windows Memory Subsystem concept that ensures -// processes will not fault when touching a committed memory region. There is -// no analogue in the POSIX & Fuchsia memory API where virtual memory pages are -// best-effort allocated resources on the first touch. If -// PageAccessibilityDisposition::kRequireUpdate disposition is used, this API -// behaves in a platform-agnostic way by simulating the Windows "decommit" state -// by both discarding the region (allowing the OS to avoid swap operations) -// *and* changing the page protections so accesses fault. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -void DecommitSystemPages( - uintptr_t address, - size_t length, - PageAccessibilityDisposition accessibility_disposition); -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -void DecommitSystemPages( - void* address, - size_t length, - PageAccessibilityDisposition accessibility_disposition); - -// Decommits one or more system pages starting at |address| and continuing for -// |length| bytes. |address| and |length| must be aligned to a system page -// boundary. -// -// In contrast to |DecommitSystemPages|, this API guarantees that the pages are -// zeroed and will always mark the region as inaccessible (the equivalent of -// setting them to PageAccessibilityConfiguration::kInaccessible). -// -// This API will crash if the operation cannot be performed. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -void DecommitAndZeroSystemPages(uintptr_t address, size_t length); -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -void DecommitAndZeroSystemPages(void* address, size_t length); - -// Whether decommitted memory is guaranteed to be zeroed when it is -// recommitted. Do not assume that this will not change over time. -constexpr PA_COMPONENT_EXPORT( - PARTITION_ALLOC) bool DecommittedMemoryIsAlwaysZeroed() { -#if BUILDFLAG(IS_APPLE) - return false; -#else - return true; -#endif -} - -// (Re)Commits one or more system pages, starting at |address| and continuing -// for |length| bytes with the given |page_accessibility| (must not be -// PageAccessibilityConfiguration::kInaccessible). |address| and |length| -// must be aligned to a system page boundary. -// -// This API will crash if the operation cannot be performed! -// -// If disposition is PageAccessibilityConfiguration::kRequireUpdate, the calls -// updates the pages to |page_accessibility|. This can be used regardless of -// what disposition was used to decommit the pages. -// PageAccessibilityConfiguration::kAllowKeepForPerf allows the implementation -// to leave the page permissions, if that improves performance. This option can -// only be used if the pages were previously accessible and decommitted with -// that same option. -// -// The memory will be zeroed when it is committed for the first time. However, -// there is no such guarantee when memory is recommitted, unless -// |DecommittedMemoryIsAlwaysZeroed()| is true. -// -// This operation may not be atomic on some platforms. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -void RecommitSystemPages( - uintptr_t address, - size_t length, - PageAccessibilityConfiguration page_accessibility, - PageAccessibilityDisposition accessibility_disposition); - -// Like RecommitSystemPages(), but returns false instead of crashing. -[[nodiscard]] PA_COMPONENT_EXPORT(PARTITION_ALLOC) bool TryRecommitSystemPages( - uintptr_t address, - size_t length, - PageAccessibilityConfiguration page_accessibility, - PageAccessibilityDisposition accessibility_disposition); - -// Discard one or more system pages starting at |address| and continuing for -// |length| bytes. |length| must be a multiple of |SystemPageSize()|. -// -// Discarding is a hint to the system that the page is no longer required. The -// hint may: -// - Do nothing. -// - Discard the page immediately, freeing up physical pages. -// - Discard the page at some time in the future in response to memory -// pressure. -// -// Only committed pages should be discarded. Discarding a page does not decommit -// it, and it is valid to discard an already-discarded page. A read or write to -// a discarded page will not fault. -// -// Reading from a discarded page may return the original page content, or a page -// full of zeroes. -// -// Writing to a discarded page is the only guaranteed way to tell the system -// that the page is required again. Once written to, the content of the page is -// guaranteed stable once more. After being written to, the page content may be -// based on the original page content, or a page of zeroes. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -void DiscardSystemPages(uintptr_t address, size_t length); -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -void DiscardSystemPages(void* address, size_t length); - -// Rounds up |address| to the next multiple of |SystemPageSize()|. Returns -// 0 for an |address| of 0. -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t -RoundUpToSystemPage(uintptr_t address) { - return (address + internal::SystemPageOffsetMask()) & - internal::SystemPageBaseMask(); -} - -// Rounds down |address| to the previous multiple of |SystemPageSize()|. Returns -// 0 for an |address| of 0. -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t -RoundDownToSystemPage(uintptr_t address) { - return address & internal::SystemPageBaseMask(); -} - -// Rounds up |address| to the next multiple of |PageAllocationGranularity()|. -// Returns 0 for an |address| of 0. -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t -RoundUpToPageAllocationGranularity(uintptr_t address) { - return (address + internal::PageAllocationGranularityOffsetMask()) & - internal::PageAllocationGranularityBaseMask(); -} - -// Rounds down |address| to the previous multiple of -// |PageAllocationGranularity()|. Returns 0 for an |address| of 0. -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR uintptr_t -RoundDownToPageAllocationGranularity(uintptr_t address) { - return address & internal::PageAllocationGranularityBaseMask(); -} - -// Reserves (at least) |size| bytes of address space, aligned to -// |PageAllocationGranularity()|. This can be called early on to make it more -// likely that large allocations will succeed. Returns true if the reservation -// succeeded, false if the reservation failed or a reservation was already made. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) bool ReserveAddressSpace(size_t size); - -// Releases any reserved address space. |AllocPages| calls this automatically on -// an allocation failure. External allocators may also call this on failure. -// -// Returns true when an existing reservation was released. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) bool ReleaseReservation(); - -// Returns true if there is currently an address space reservation. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) bool HasReservationForTesting(); - -// Returns |errno| (POSIX) or the result of |GetLastError| (Windows) when |mmap| -// (POSIX) or |VirtualAlloc| (Windows) fails. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) uint32_t GetAllocPageErrorCode(); - -// Returns the total amount of mapped pages from all clients of -// PageAllocator. These pages may or may not be committed. This is mostly useful -// to assess address space pressure. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) size_t GetTotalMappedSize(); - -#if BUILDFLAG(IS_WIN) -// Sets whether to retry the allocation of pages when a commit failure -// happens. This doesn't cover cases where the system is out of address space, -// or reaches another limit. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -void SetRetryOnCommitFailure(bool retry_on_commit_failure); -bool GetRetryOnCommitFailure(); -#endif // BUILDFLAG(IS_WIN) - -} // namespace partition_alloc - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_H_
diff --git a/base/allocator/partition_allocator/page_allocator_constants.h b/base/allocator/partition_allocator/page_allocator_constants.h deleted file mode 100644 index 37a88fc..0000000 --- a/base/allocator/partition_allocator/page_allocator_constants.h +++ /dev/null
@@ -1,169 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_CONSTANTS_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_CONSTANTS_H_ - -#include <stddef.h> - -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "build/build_config.h" - -#if BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_64_BITS) - -#include <mach/vm_page_size.h> - -// Although page allocator constants are not constexpr, they are run-time -// constant. Because the underlying variables they access, such as vm_page_size, -// are not marked const, the compiler normally has no way to know that they -// don’t change and must obtain their values whenever it can't prove that they -// haven't been modified, even if they had already been obtained previously. -// Attaching __attribute__((const)) to these declarations allows these redundant -// accesses to be omitted under optimization such as common subexpression -// elimination. -#define PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR __attribute__((const)) - -#elif BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64) -// This should work for all POSIX (if needed), but currently all other -// supported OS/architecture combinations use either hard-coded values -// (such as x86) or have means to determine these values without needing -// atomics (such as macOS on arm64). - -// Page allocator constants are run-time constant -#define PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR __attribute__((const)) - -#include <unistd.h> -#include <atomic> - -namespace partition_alloc::internal { - -// Holds the current page size and shift, where size = 1 << shift -// Use PageAllocationGranularity(), PageAllocationGranularityShift() -// to initialize and retrieve these values safely. -struct PageCharacteristics { - std::atomic<size_t> size; - std::atomic<size_t> shift; -}; -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -extern PageCharacteristics page_characteristics; - -} // namespace partition_alloc::internal - -#else - -// When defined, page size constants are fixed at compile time. When not -// defined, they may vary at run time. -#define PAGE_ALLOCATOR_CONSTANTS_ARE_CONSTEXPR 1 - -// Use this macro to declare a function as constexpr or not based on whether -// PAGE_ALLOCATOR_CONSTANTS_ARE_CONSTEXPR is defined. -#define PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR constexpr - -#endif - -namespace partition_alloc::internal { - -// Forward declaration, implementation below -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t -PageAllocationGranularity(); - -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t -PageAllocationGranularityShift() { -#if BUILDFLAG(IS_WIN) || defined(ARCH_CPU_PPC64) - // Modern ppc64 systems support 4kB (shift = 12) and 64kB (shift = 16) page - // sizes. Since 64kB is the de facto standard on the platform and binaries - // compiled for 64kB are likely to work on 4kB systems, 64kB is a good choice - // here. - return 16; // 64kB -#elif defined(_MIPS_ARCH_LOONGSON) || defined(ARCH_CPU_LOONG64) - return 14; // 16kB -#elif BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_64_BITS) - return static_cast<size_t>(vm_page_shift); -#elif BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64) - // arm64 supports 4kb (shift = 12), 16kb (shift = 14), and 64kb (shift = 16) - // page sizes. Retrieve from or initialize cache. - size_t shift = page_characteristics.shift.load(std::memory_order_relaxed); - if (PA_UNLIKELY(shift == 0)) { - shift = static_cast<size_t>( - __builtin_ctz((unsigned int)PageAllocationGranularity())); - page_characteristics.shift.store(shift, std::memory_order_relaxed); - } - return shift; -#else - return 12; // 4kB -#endif -} - -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t -PageAllocationGranularity() { -#if BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_64_BITS) - // This is literally equivalent to |1 << PageAllocationGranularityShift()| - // below, but was separated out for IS_APPLE to avoid << on a non-constexpr. - return vm_page_size; -#elif BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64) - // arm64 supports 4kb, 16kb, and 64kb page sizes. Retrieve from or - // initialize cache. - size_t size = page_characteristics.size.load(std::memory_order_relaxed); - if (PA_UNLIKELY(size == 0)) { - size = static_cast<size_t>(getpagesize()); - page_characteristics.size.store(size, std::memory_order_relaxed); - } - return size; -#else - return 1 << PageAllocationGranularityShift(); -#endif -} - -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t -PageAllocationGranularityOffsetMask() { - return PageAllocationGranularity() - 1; -} - -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t -PageAllocationGranularityBaseMask() { - return ~PageAllocationGranularityOffsetMask(); -} - -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t -SystemPageShift() { - // On Windows allocation granularity is higher than the page size. This comes - // into play when reserving address space range (allocation granularity), - // compared to committing pages into memory (system page granularity). -#if BUILDFLAG(IS_WIN) - return 12; // 4096=1<<12 -#else - return PageAllocationGranularityShift(); -#endif -} - -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t -SystemPageSize() { -#if (BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_64_BITS)) || \ - (BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64)) - // This is literally equivalent to |1 << SystemPageShift()| below, but was - // separated out for 64-bit IS_APPLE and arm64 on Linux to avoid << on a - // non-constexpr. - return PageAllocationGranularity(); -#else - return 1 << SystemPageShift(); -#endif -} - -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t -SystemPageOffsetMask() { - return SystemPageSize() - 1; -} - -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t -SystemPageBaseMask() { - return ~SystemPageOffsetMask(); -} - -constexpr size_t kPageMetadataShift = 5; // 32 bytes per partition page. -constexpr size_t kPageMetadataSize = 1 << kPageMetadataShift; - -} // namespace partition_alloc::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_CONSTANTS_H_
diff --git a/base/allocator/partition_allocator/page_allocator_internal.h b/base/allocator/partition_allocator/page_allocator_internal.h deleted file mode 100644 index 1ae26c5..0000000 --- a/base/allocator/partition_allocator/page_allocator_internal.h +++ /dev/null
@@ -1,23 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNAL_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNAL_H_ - -#include <cstddef> -#include <cstdint> - -#include "base/allocator/partition_allocator/page_allocator.h" - -namespace partition_alloc::internal { - -uintptr_t SystemAllocPages(uintptr_t hint, - size_t length, - PageAccessibilityConfiguration accessibility, - PageTag page_tag, - int file_descriptor_for_shared_alloc = -1); - -} // namespace partition_alloc::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNAL_H_
diff --git a/base/allocator/partition_allocator/page_allocator_internals_fuchsia.h b/base/allocator/partition_allocator/page_allocator_internals_fuchsia.h deleted file mode 100644 index eba276e..0000000 --- a/base/allocator/partition_allocator/page_allocator_internals_fuchsia.h +++ /dev/null
@@ -1,233 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// -// This file implements memory allocation primitives for PageAllocator using -// Fuchsia's VMOs (Virtual Memory Objects). VMO API is documented in -// https://fuchsia.dev/fuchsia-src/zircon/objects/vm_object . A VMO is a kernel -// object that corresponds to a set of memory pages. VMO pages may be mapped -// to an address space. The code below creates VMOs for each memory allocations -// and maps them to the default address space of the current process. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_FUCHSIA_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_FUCHSIA_H_ - -#include <lib/zx/vmar.h> -#include <lib/zx/vmo.h> - -#include <cstdint> - -#include "base/allocator/partition_allocator/page_allocator.h" -#include "base/allocator/partition_allocator/partition_alloc_base/fuchsia/fuchsia_logging.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/partition_alloc_notreached.h" - -namespace partition_alloc::internal { - -namespace { - -// Returns VMO name for a PageTag. -const char* PageTagToName(PageTag tag) { - switch (tag) { - case PageTag::kBlinkGC: - return "cr_blink_gc"; - case PageTag::kPartitionAlloc: - return "cr_partition_alloc"; - case PageTag::kChromium: - return "cr_chromium"; - case PageTag::kV8: - return "cr_v8"; - default: - PA_DCHECK(false); - return ""; - } -} - -zx_vm_option_t PageAccessibilityToZxVmOptions( - PageAccessibilityConfiguration accessibility) { - switch (accessibility.permissions) { - case PageAccessibilityConfiguration::kRead: - return ZX_VM_PERM_READ; - case PageAccessibilityConfiguration::kReadWrite: - case PageAccessibilityConfiguration::kReadWriteTagged: - return ZX_VM_PERM_READ | ZX_VM_PERM_WRITE; - case PageAccessibilityConfiguration::kReadExecuteProtected: - case PageAccessibilityConfiguration::kReadExecute: - return ZX_VM_PERM_READ | ZX_VM_PERM_EXECUTE; - case PageAccessibilityConfiguration::kReadWriteExecute: - return ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_PERM_EXECUTE; - default: - PA_NOTREACHED(); - [[fallthrough]]; - case PageAccessibilityConfiguration::kInaccessible: - return 0; - } -} - -} // namespace - -// zx_vmar_map() will fail if the VMO cannot be mapped at |vmar_offset|, i.e. -// |hint| is not advisory. -constexpr bool kHintIsAdvisory = false; - -std::atomic<int32_t> s_allocPageErrorCode{0}; - -uintptr_t SystemAllocPagesInternal( - uintptr_t hint, - size_t length, - PageAccessibilityConfiguration accessibility, - PageTag page_tag, - [[maybe_unused]] int file_descriptor_for_shared_alloc) { - zx::vmo vmo; - zx_status_t status = zx::vmo::create(length, 0, &vmo); - if (status != ZX_OK) { - PA_ZX_DLOG(INFO, status) << "zx_vmo_create"; - return 0; - } - - const char* vmo_name = PageTagToName(page_tag); - status = vmo.set_property(ZX_PROP_NAME, vmo_name, strlen(vmo_name)); - - // VMO names are used only for debugging, so failure to set a name is not - // fatal. - PA_ZX_DCHECK(status == ZX_OK, status); - - if (page_tag == PageTag::kV8) { - // V8 uses JIT. Call zx_vmo_replace_as_executable() to allow code execution - // in the new VMO. - status = vmo.replace_as_executable(zx::resource(), &vmo); - if (status != ZX_OK) { - PA_ZX_DLOG(INFO, status) << "zx_vmo_replace_as_executable"; - return 0; - } - } - - zx_vm_option_t options = PageAccessibilityToZxVmOptions(accessibility); - - uint64_t vmar_offset = 0; - if (hint) { - vmar_offset = hint; - options |= ZX_VM_SPECIFIC; - } - - uint64_t address; - status = zx::vmar::root_self()->map(options, vmar_offset, vmo, - /*vmo_offset=*/0, length, &address); - if (status != ZX_OK) { - // map() is expected to fail if |hint| is set to an already-in-use location. - if (!hint) { - PA_ZX_DLOG(ERROR, status) << "zx_vmar_map"; - } - return 0; - } - - return address; -} - -uintptr_t TrimMappingInternal(uintptr_t base_address, - size_t base_length, - size_t trim_length, - PageAccessibilityConfiguration accessibility, - size_t pre_slack, - size_t post_slack) { - PA_DCHECK(base_length == trim_length + pre_slack + post_slack); - - // Unmap head if necessary. - if (pre_slack) { - zx_status_t status = zx::vmar::root_self()->unmap(base_address, pre_slack); - PA_ZX_CHECK(status == ZX_OK, status); - } - - // Unmap tail if necessary. - if (post_slack) { - zx_status_t status = zx::vmar::root_self()->unmap( - base_address + pre_slack + trim_length, post_slack); - PA_ZX_CHECK(status == ZX_OK, status); - } - - return base_address + pre_slack; -} - -bool TrySetSystemPagesAccessInternal( - uint64_t address, - size_t length, - PageAccessibilityConfiguration accessibility) { - zx_status_t status = zx::vmar::root_self()->protect( - PageAccessibilityToZxVmOptions(accessibility), address, length); - return status == ZX_OK; -} - -void SetSystemPagesAccessInternal( - uint64_t address, - size_t length, - PageAccessibilityConfiguration accessibility) { - zx_status_t status = zx::vmar::root_self()->protect( - PageAccessibilityToZxVmOptions(accessibility), address, length); - PA_ZX_CHECK(status == ZX_OK, status); -} - -void FreePagesInternal(uint64_t address, size_t length) { - zx_status_t status = zx::vmar::root_self()->unmap(address, length); - PA_ZX_CHECK(status == ZX_OK, status); -} - -void DiscardSystemPagesInternal(uint64_t address, size_t length) { - zx_status_t status = zx::vmar::root_self()->op_range( - ZX_VMO_OP_DECOMMIT, address, length, nullptr, 0); - PA_ZX_CHECK(status == ZX_OK, status); -} - -void DecommitSystemPagesInternal( - uint64_t address, - size_t length, - PageAccessibilityDisposition accessibility_disposition) { - if (accessibility_disposition == - PageAccessibilityDisposition::kRequireUpdate) { - SetSystemPagesAccess(address, length, - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kInaccessible)); - } - - DiscardSystemPagesInternal(address, length); -} - -void DecommitAndZeroSystemPagesInternal(uintptr_t address, size_t length) { - SetSystemPagesAccess(address, length, - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kInaccessible)); - - DiscardSystemPagesInternal(address, length); -} - -void RecommitSystemPagesInternal( - uintptr_t address, - size_t length, - PageAccessibilityConfiguration accessibility, - PageAccessibilityDisposition accessibility_disposition) { - // On Fuchsia systems, the caller needs to simply read the memory to recommit - // it. However, if decommit changed the permissions, recommit has to change - // them back. - if (accessibility_disposition == - PageAccessibilityDisposition::kRequireUpdate) { - SetSystemPagesAccess(address, length, accessibility); - } -} - -bool TryRecommitSystemPagesInternal( - uintptr_t address, - size_t length, - PageAccessibilityConfiguration accessibility, - PageAccessibilityDisposition accessibility_disposition) { - // On Fuchsia systems, the caller needs to simply read the memory to recommit - // it. However, if decommit changed the permissions, recommit has to change - // them back. - if (accessibility_disposition == - PageAccessibilityDisposition::kRequireUpdate) { - return TrySetSystemPagesAccess(address, length, accessibility); - } - return true; -} - -} // namespace partition_alloc::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_FUCHSIA_H_
diff --git a/base/allocator/partition_allocator/page_allocator_internals_posix.cc b/base/allocator/partition_allocator/page_allocator_internals_posix.cc deleted file mode 100644 index ca084e1..0000000 --- a/base/allocator/partition_allocator/page_allocator_internals_posix.cc +++ /dev/null
@@ -1,43 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/page_allocator.h" -#include "base/allocator/partition_allocator/partition_alloc_base/cpu.h" -#include "base/allocator/partition_allocator/partition_alloc_notreached.h" - -#include <sys/mman.h> - -// PA_PROT_BTI requests a page that supports BTI landing pads. -#define PA_PROT_BTI 0x10 -// PA_PROT_MTE requests a page that's suitable for memory tagging. -#define PA_PROT_MTE 0x20 - -namespace partition_alloc::internal { - -int GetAccessFlags(PageAccessibilityConfiguration accessibility) { - switch (accessibility.permissions) { - case PageAccessibilityConfiguration::kRead: - return PROT_READ; - case PageAccessibilityConfiguration::kReadWriteTagged: -#if defined(ARCH_CPU_ARM64) - return PROT_READ | PROT_WRITE | - (base::CPU::GetInstanceNoAllocation().has_mte() ? PA_PROT_MTE : 0); -#else - [[fallthrough]]; -#endif - case PageAccessibilityConfiguration::kReadWrite: - return PROT_READ | PROT_WRITE; - case PageAccessibilityConfiguration::kReadExecuteProtected: - return PROT_READ | PROT_EXEC | - (base::CPU::GetInstanceNoAllocation().has_bti() ? PA_PROT_BTI : 0); - case PageAccessibilityConfiguration::kReadExecute: - return PROT_READ | PROT_EXEC; - case PageAccessibilityConfiguration::kReadWriteExecute: - return PROT_READ | PROT_WRITE | PROT_EXEC; - case PageAccessibilityConfiguration::kInaccessible: - return PROT_NONE; - } -} - -} // namespace partition_alloc::internal
diff --git a/base/allocator/partition_allocator/page_allocator_internals_posix.h b/base/allocator/partition_allocator/page_allocator_internals_posix.h deleted file mode 100644 index 30009c9..0000000 --- a/base/allocator/partition_allocator/page_allocator_internals_posix.h +++ /dev/null
@@ -1,410 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_POSIX_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_POSIX_H_ - -#include <algorithm> -#include <cerrno> -#include <cstdint> -#include <cstring> - -#include <sys/mman.h> - -#include "base/allocator/partition_allocator/oom.h" -#include "base/allocator/partition_allocator/page_allocator.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_base/posix/eintr_wrapper.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/pkey.h" -#include "build/build_config.h" - -#if BUILDFLAG(IS_APPLE) -#include "base/allocator/partition_allocator/partition_alloc_base/mac/foundation_util.h" -#if BUILDFLAG(IS_IOS) -#include "base/allocator/partition_allocator/partition_alloc_base/ios/ios_util.h" -#elif BUILDFLAG(IS_MAC) -#include "base/allocator/partition_allocator/partition_alloc_base/mac/mac_util.h" -#else -#error "Unknown platform" -#endif -#include "base/allocator/partition_allocator/partition_alloc_base/mac/scoped_cftyperef.h" - -#include <Availability.h> -#include <Security/Security.h> -#include <mach/mach.h> -#endif -#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) -#include <sys/prctl.h> -#endif -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) -#include <sys/resource.h> -#endif - -#ifndef MAP_ANONYMOUS -#define MAP_ANONYMOUS MAP_ANON -#endif - -#if BUILDFLAG(IS_MAC) - -// SecTaskGetCodeSignStatus is marked as unavailable on macOS, although it’s -// available on iOS and other Apple operating systems. It is, in fact, present -// on the system since macOS 10.12. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wavailability" -uint32_t SecTaskGetCodeSignStatus(SecTaskRef task) API_AVAILABLE(macos(10.12)); -#pragma clang diagnostic pop - -#endif // BUILDFLAG(IS_MAC) - -namespace partition_alloc::internal { - -namespace { - -#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) -#if defined(PR_SET_VMA) && defined(PR_SET_VMA_ANON_NAME) -const char* PageTagToName(PageTag tag) { - // Important: All the names should be string literals. As per prctl.h in - // //third_party/android_ndk the kernel keeps a pointer to the name instead - // of copying it. - // - // Having the name in .rodata ensures that the pointer remains valid as - // long as the mapping is alive. - switch (tag) { - case PageTag::kBlinkGC: - return "blink_gc"; - case PageTag::kPartitionAlloc: - return "partition_alloc"; - case PageTag::kChromium: - return "chromium"; - case PageTag::kV8: - return "v8"; - default: - PA_DCHECK(false); - return ""; - } -} -#endif -#endif // BUILDFLAG(IS_ANDROID) - -#if BUILDFLAG(IS_MAC) -// Tests whether the version of macOS supports the MAP_JIT flag and if the -// current process is signed with the hardened runtime and the allow-jit -// entitlement, returning whether MAP_JIT should be used to allocate regions -// that will contain JIT-compiled executable code. -bool UseMapJit() { - if (!base::mac::IsAtLeastOS10_14()) { - // MAP_JIT existed before macOS 10.14, but had somewhat different semantics. - // Only one MAP_JIT region was permitted per process, but calling code here - // will very likely require more than one such region. Since MAP_JIT is not - // strictly necessary to write code to a region and then execute it on these - // older OSes, don’t use it at all. - return false; - } - - // Until determining that the hardened runtime is enabled, early returns will - // return true, so that MAP_JIT will be used. This is important on arm64, - // which only allows pages to be simultaneously writable and executable when - // in a region allocated with MAP_JIT, regardless of code signing options. On - // arm64, an attempt to set a non-MAP_JIT page as simultaneously writable and - // executable fails with EPERM. Although this is not enforced on x86_64, - // MAP_JIT is harmless in that case. - - base::ScopedCFTypeRef<SecTaskRef> task( - SecTaskCreateFromSelf(kCFAllocatorDefault)); - if (!task) { - return true; - } - - uint32_t flags = SecTaskGetCodeSignStatus(task); - if (!(flags & kSecCodeSignatureRuntime)) { - // The hardened runtime is not enabled. Note that kSecCodeSignatureRuntime - // == CS_RUNTIME. - return true; - } - - // The hardened runtime is enabled. From this point on, early returns must - // return false, indicating that MAP_JIT is not to be used. It’s an error - // (EINVAL) to use MAP_JIT with the hardened runtime unless the JIT - // entitlement is specified. - - base::ScopedCFTypeRef<CFTypeRef> jit_entitlement( - SecTaskCopyValueForEntitlement( - task.get(), CFSTR("com.apple.security.cs.allow-jit"), nullptr)); - if (!jit_entitlement) { - return false; - } - - return base::mac::CFCast<CFBooleanRef>(jit_entitlement.get()) == - kCFBooleanTrue; -} -#elif BUILDFLAG(IS_IOS) -bool UseMapJit() { -// Always enable MAP_JIT in simulator as it is supported unconditionally. -#if TARGET_IPHONE_SIMULATOR - return true; -#else - // TODO(https://crbug.com/1413818): Fill this out when the API it is - // available. - return false; -#endif // TARGET_IPHONE_SIMULATOR -} -#endif // BUILDFLAG(IS_IOS) -} // namespace - -// |mmap| uses a nearby address if the hint address is blocked. -constexpr bool kHintIsAdvisory = true; -std::atomic<int32_t> s_allocPageErrorCode{0}; - -int GetAccessFlags(PageAccessibilityConfiguration accessibility); - -uintptr_t SystemAllocPagesInternal(uintptr_t hint, - size_t length, - PageAccessibilityConfiguration accessibility, - PageTag page_tag, - int file_descriptor_for_shared_alloc) { -#if BUILDFLAG(IS_APPLE) - // Use a custom tag to make it easier to distinguish Partition Alloc regions - // in vmmap(1). Tags between 240-255 are supported. - PA_DCHECK(PageTag::kFirst <= page_tag); - PA_DCHECK(PageTag::kLast >= page_tag); - int fd = file_descriptor_for_shared_alloc == -1 - ? VM_MAKE_TAG(static_cast<int>(page_tag)) - : file_descriptor_for_shared_alloc; -#else - int fd = file_descriptor_for_shared_alloc; -#endif - - int access_flag = GetAccessFlags(accessibility); - int map_flags = MAP_ANONYMOUS | MAP_PRIVATE; - -#if BUILDFLAG(IS_APPLE) - // On macOS 10.14 and higher, executables that are code signed with the - // "runtime" option cannot execute writable memory by default. They can opt - // into this capability by specifying the "com.apple.security.cs.allow-jit" - // code signing entitlement and allocating the region with the MAP_JIT flag. - static const bool kUseMapJit = UseMapJit(); - if (page_tag == PageTag::kV8 && kUseMapJit) { - map_flags |= MAP_JIT; - } -#endif - - void* ret = mmap(reinterpret_cast<void*>(hint), length, access_flag, - map_flags, fd, 0); - if (ret == MAP_FAILED) { - s_allocPageErrorCode = errno; - ret = nullptr; - } - -#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) -#if defined(PR_SET_VMA) && defined(PR_SET_VMA_ANON_NAME) - // On Android and Linux, anonymous mappings can have a name attached to them. - // This is useful for debugging, and double-checking memory attribution. - if (ret) { - // No error checking on purpose, testing only. - prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, ret, length, - PageTagToName(page_tag)); - } -#endif -#endif - - return reinterpret_cast<uintptr_t>(ret); -} - -bool TrySetSystemPagesAccessInternal( - uintptr_t address, - size_t length, - PageAccessibilityConfiguration accessibility) { -#if BUILDFLAG(ENABLE_PKEYS) - return 0 == PkeyMprotectIfEnabled(reinterpret_cast<void*>(address), length, - GetAccessFlags(accessibility), - accessibility.pkey); -#else - return 0 == PA_HANDLE_EINTR(mprotect(reinterpret_cast<void*>(address), length, - GetAccessFlags(accessibility))); -#endif -} - -void SetSystemPagesAccessInternal( - uintptr_t address, - size_t length, - PageAccessibilityConfiguration accessibility) { - int access_flags = GetAccessFlags(accessibility); -#if BUILDFLAG(ENABLE_PKEYS) - int ret = - PkeyMprotectIfEnabled(reinterpret_cast<void*>(address), length, - GetAccessFlags(accessibility), accessibility.pkey); -#else - int ret = PA_HANDLE_EINTR(mprotect(reinterpret_cast<void*>(address), length, - GetAccessFlags(accessibility))); -#endif - - // On Linux, man mprotect(2) states that ENOMEM is returned when (1) internal - // kernel data structures cannot be allocated, (2) the address range is - // invalid, or (3) this would split an existing mapping in a way that would - // exceed the maximum number of allowed mappings. - // - // Neither are very likely, but we still get a lot of crashes here. This is - // because setrlimit(RLIMIT_DATA)'s limit is checked and enforced here, if the - // access flags match a "data" mapping, which in our case would be MAP_PRIVATE - // | MAP_ANONYMOUS, and PROT_WRITE. see the call to may_expand_vm() in - // mm/mprotect.c in the kernel for details. - // - // In this case, we are almost certainly bumping into the sandbox limit, mark - // the crash as OOM. See SandboxLinux::LimitAddressSpace() for details. - if (ret == -1 && errno == ENOMEM && (access_flags & PROT_WRITE)) { - OOM_CRASH(length); - } - - PA_PCHECK(0 == ret); -} - -void FreePagesInternal(uintptr_t address, size_t length) { - PA_PCHECK(0 == munmap(reinterpret_cast<void*>(address), length)); -} - -uintptr_t TrimMappingInternal(uintptr_t base_address, - size_t base_length, - size_t trim_length, - PageAccessibilityConfiguration accessibility, - size_t pre_slack, - size_t post_slack) { - uintptr_t ret = base_address; - // We can resize the allocation run. Release unneeded memory before and after - // the aligned range. - if (pre_slack) { - FreePages(base_address, pre_slack); - ret = base_address + pre_slack; - } - if (post_slack) { - FreePages(ret + trim_length, post_slack); - } - return ret; -} - -void DecommitSystemPagesInternal( - uintptr_t address, - size_t length, - PageAccessibilityDisposition accessibility_disposition) { - // In POSIX, there is no decommit concept. Discarding is an effective way of - // implementing the Windows semantics where the OS is allowed to not swap the - // pages in the region. - DiscardSystemPages(address, length); - - bool change_permissions = - accessibility_disposition == PageAccessibilityDisposition::kRequireUpdate; -#if BUILDFLAG(PA_DCHECK_IS_ON) - // This is not guaranteed, show that we're serious. - // - // More specifically, several callers have had issues with assuming that - // memory is zeroed, this would hopefully make these bugs more visible. We - // don't memset() everything, because ranges can be very large, and doing it - // over the entire range could make Chrome unusable with - // BUILDFLAG(PA_DCHECK_IS_ON). - // - // Only do it when we are about to change the permissions, since we don't know - // the previous permissions, and cannot restore them. - if (!DecommittedMemoryIsAlwaysZeroed() && change_permissions) { - // Memory may not be writable. - size_t size = std::min(length, 2 * SystemPageSize()); - void* ptr = reinterpret_cast<void*>(address); - PA_CHECK(mprotect(ptr, size, PROT_WRITE) == 0); - memset(ptr, 0xcc, size); - } -#endif - - // Make pages inaccessible, unless the caller requested to keep permissions. - // - // Note, there is a small window between these calls when the pages can be - // incorrectly touched and brought back to memory. Not ideal, but doing those - // operations in the opposite order resulted in PMF regression on Mac (see - // crbug.com/1153021). - if (change_permissions) { - SetSystemPagesAccess(address, length, - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kInaccessible)); - } -} - -void DecommitAndZeroSystemPagesInternal(uintptr_t address, size_t length) { - // https://pubs.opengroup.org/onlinepubs/9699919799/functions/mmap.html: "If - // a MAP_FIXED request is successful, then any previous mappings [...] for - // those whole pages containing any part of the address range [pa,pa+len) - // shall be removed, as if by an appropriate call to munmap(), before the - // new mapping is established." As a consequence, the memory will be - // zero-initialized on next access. - void* ptr = reinterpret_cast<void*>(address); - void* ret = mmap(ptr, length, PROT_NONE, - MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - PA_CHECK(ptr == ret); -} - -void RecommitSystemPagesInternal( - uintptr_t address, - size_t length, - PageAccessibilityConfiguration accessibility, - PageAccessibilityDisposition accessibility_disposition) { - // On POSIX systems, the caller needs to simply read the memory to recommit - // it. However, if decommit changed the permissions, recommit has to change - // them back. - if (accessibility_disposition == - PageAccessibilityDisposition::kRequireUpdate) { - SetSystemPagesAccess(address, length, accessibility); - } - -#if BUILDFLAG(IS_APPLE) - // On macOS, to update accounting, we need to make another syscall. For more - // details, see https://crbug.com/823915. - madvise(reinterpret_cast<void*>(address), length, MADV_FREE_REUSE); -#endif -} - -bool TryRecommitSystemPagesInternal( - uintptr_t address, - size_t length, - PageAccessibilityConfiguration accessibility, - PageAccessibilityDisposition accessibility_disposition) { - // On POSIX systems, the caller needs to simply read the memory to recommit - // it. However, if decommit changed the permissions, recommit has to change - // them back. - if (accessibility_disposition == - PageAccessibilityDisposition::kRequireUpdate) { - bool ok = TrySetSystemPagesAccess(address, length, accessibility); - if (!ok) { - return false; - } - } - -#if BUILDFLAG(IS_APPLE) - // On macOS, to update accounting, we need to make another syscall. For more - // details, see https://crbug.com/823915. - madvise(reinterpret_cast<void*>(address), length, MADV_FREE_REUSE); -#endif - - return true; -} - -void DiscardSystemPagesInternal(uintptr_t address, size_t length) { - void* ptr = reinterpret_cast<void*>(address); -#if BUILDFLAG(IS_APPLE) - int ret = madvise(ptr, length, MADV_FREE_REUSABLE); - if (ret) { - // MADV_FREE_REUSABLE sometimes fails, so fall back to MADV_DONTNEED. - ret = madvise(ptr, length, MADV_DONTNEED); - } - PA_PCHECK(ret == 0); -#else // BUILDFLAG(IS_APPLE) - // We have experimented with other flags, but with suboptimal results. - // - // MADV_FREE (Linux): Makes our memory measurements less predictable; - // performance benefits unclear. - // - // Therefore, we just do the simple thing: MADV_DONTNEED. - PA_PCHECK(0 == madvise(ptr, length, MADV_DONTNEED)); -#endif // BUILDFLAG(IS_APPLE) -} - -} // namespace partition_alloc::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_POSIX_H_
diff --git a/base/allocator/partition_allocator/page_allocator_internals_win.h b/base/allocator/partition_allocator/page_allocator_internals_win.h deleted file mode 100644 index 21ca31d..0000000 --- a/base/allocator/partition_allocator/page_allocator_internals_win.h +++ /dev/null
@@ -1,243 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_WIN_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_WIN_H_ - -#include <cstdint> - -#include "base/allocator/partition_allocator/oom.h" -#include "base/allocator/partition_allocator/page_allocator.h" -#include "base/allocator/partition_allocator/page_allocator_internal.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/partition_alloc_notreached.h" - -namespace partition_alloc::internal { - -// |VirtualAlloc| will fail if allocation at the hint address is blocked. -constexpr bool kHintIsAdvisory = false; -std::atomic<int32_t> s_allocPageErrorCode{ERROR_SUCCESS}; - -bool IsOutOfMemory(DWORD error) { - // From - // https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499- - switch (error) { - // Page file is being extended. - case ERROR_COMMITMENT_MINIMUM: - // Page file is too small. - case ERROR_COMMITMENT_LIMIT: -#if BUILDFLAG(HAS_64_BIT_POINTERS) - // Not enough memory resources are available to process this command. - // - // It is not entirely clear whether this error pertains to out of address - // space errors, or the kernel being out of memory. Only include it for 64 - // bit architectures, since address space issues are unlikely there. - case ERROR_NOT_ENOUGH_MEMORY: -#endif - case ERROR_PAGEFILE_QUOTA: - // Insufficient quota to complete the requested service. - return true; - default: - return false; - } -} - -void* VirtualAllocWithRetry(void* address, - size_t size, - DWORD type_flags, - DWORD access_flags) { - void* ret = nullptr; - // Failure to commit memory can be temporary, in at least two cases: - // - The page file is getting extended. - // - Another process terminates (most likely because of OOM) - // - // Wait and retry, since the alternative is crashing. Note that if we - // selectively apply this... hum... beautiful hack to some process types only, - // "some process crashing" may very well be one of ours, which may be - // desirable (e.g. some processes like the browser are more important than - // others). - // - // This approach has been shown to be effective for Firefox, see - // crbug.com/1392738 for context. Constants below are accordingly taken from - // Firefox as well. - constexpr int kMaxTries = 10; - constexpr int kDelayMs = 50; - - bool should_retry = GetRetryOnCommitFailure() && (type_flags & MEM_COMMIT) && - (access_flags != PAGE_NOACCESS); - for (int tries = 0; tries < kMaxTries; tries++) { - ret = VirtualAlloc(address, size, type_flags, access_flags); - // Only retry for commit failures. If this is an address space problem - // (e.g. caller asked for an address which is not available), this is - // unlikely to be resolved by waiting. - if (ret || !should_retry || !IsOutOfMemory(GetLastError())) { - break; - } - - Sleep(kDelayMs); - } - return ret; -} - -int GetAccessFlags(PageAccessibilityConfiguration accessibility) { - switch (accessibility.permissions) { - case PageAccessibilityConfiguration::kRead: - return PAGE_READONLY; - case PageAccessibilityConfiguration::kReadWrite: - case PageAccessibilityConfiguration::kReadWriteTagged: - return PAGE_READWRITE; - case PageAccessibilityConfiguration::kReadExecute: - case PageAccessibilityConfiguration::kReadExecuteProtected: - return PAGE_EXECUTE_READ; - case PageAccessibilityConfiguration::kReadWriteExecute: - return PAGE_EXECUTE_READWRITE; - default: - PA_NOTREACHED(); - [[fallthrough]]; - case PageAccessibilityConfiguration::kInaccessible: - return PAGE_NOACCESS; - } -} - -uintptr_t SystemAllocPagesInternal( - uintptr_t hint, - size_t length, - PageAccessibilityConfiguration accessibility, - PageTag page_tag, - [[maybe_unused]] int file_descriptor_for_shared_alloc) { - DWORD access_flag = GetAccessFlags(accessibility); - const DWORD type_flags = (accessibility.permissions != - PageAccessibilityConfiguration::kInaccessible) - ? (MEM_RESERVE | MEM_COMMIT) - : MEM_RESERVE; - void* ret = VirtualAllocWithRetry(reinterpret_cast<void*>(hint), length, - type_flags, access_flag); - if (ret == nullptr) { - s_allocPageErrorCode = GetLastError(); - } - return reinterpret_cast<uintptr_t>(ret); -} - -uintptr_t TrimMappingInternal(uintptr_t base_address, - size_t base_length, - size_t trim_length, - PageAccessibilityConfiguration accessibility, - size_t pre_slack, - size_t post_slack) { - uintptr_t ret = base_address; - if (pre_slack || post_slack) { - // We cannot resize the allocation run. Free it and retry at the aligned - // address within the freed range. - ret = base_address + pre_slack; - FreePages(base_address, base_length); - ret = SystemAllocPages(ret, trim_length, accessibility, PageTag::kChromium); - } - return ret; -} - -bool TrySetSystemPagesAccessInternal( - uintptr_t address, - size_t length, - PageAccessibilityConfiguration accessibility) { - void* ptr = reinterpret_cast<void*>(address); - if (accessibility.permissions == - PageAccessibilityConfiguration::kInaccessible) { - return VirtualFree(ptr, length, MEM_DECOMMIT) != 0; - } - // Call the retry path even though this function can fail, because callers of - // this are likely to crash the process when this function fails, and we don't - // want that for transient failures. - return nullptr != VirtualAllocWithRetry(ptr, length, MEM_COMMIT, - GetAccessFlags(accessibility)); -} - -void SetSystemPagesAccessInternal( - uintptr_t address, - size_t length, - PageAccessibilityConfiguration accessibility) { - void* ptr = reinterpret_cast<void*>(address); - if (accessibility.permissions == - PageAccessibilityConfiguration::kInaccessible) { - if (!VirtualFree(ptr, length, MEM_DECOMMIT)) { - // We check `GetLastError` for `ERROR_SUCCESS` here so that in a crash - // report we get the error number. - PA_CHECK(static_cast<uint32_t>(ERROR_SUCCESS) == GetLastError()); - } - } else { - if (!VirtualAllocWithRetry(ptr, length, MEM_COMMIT, - GetAccessFlags(accessibility))) { - int32_t error = GetLastError(); - if (error == ERROR_COMMITMENT_LIMIT) { - OOM_CRASH(length); - } - // We check `GetLastError` for `ERROR_SUCCESS` here so that in a crash - // report we get the error number. - PA_CHECK(ERROR_SUCCESS == error); - } - } -} - -void FreePagesInternal(uintptr_t address, size_t length) { - PA_CHECK(VirtualFree(reinterpret_cast<void*>(address), 0, MEM_RELEASE)); -} - -void DecommitSystemPagesInternal( - uintptr_t address, - size_t length, - PageAccessibilityDisposition accessibility_disposition) { - // Ignore accessibility_disposition, because decommitting is equivalent to - // making pages inaccessible. - SetSystemPagesAccess(address, length, - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kInaccessible)); -} - -void DecommitAndZeroSystemPagesInternal(uintptr_t address, size_t length) { - // https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualfree: - // "If a page is decommitted but not released, its state changes to reserved. - // Subsequently, you can call VirtualAlloc to commit it, or VirtualFree to - // release it. Attempts to read from or write to a reserved page results in an - // access violation exception." - // https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc - // for MEM_COMMIT: "The function also guarantees that when the caller later - // initially accesses the memory, the contents will be zero." - PA_CHECK(VirtualFree(reinterpret_cast<void*>(address), length, MEM_DECOMMIT)); -} - -void RecommitSystemPagesInternal( - uintptr_t address, - size_t length, - PageAccessibilityConfiguration accessibility, - PageAccessibilityDisposition accessibility_disposition) { - // Ignore accessibility_disposition, because decommitting is equivalent to - // making pages inaccessible. - SetSystemPagesAccess(address, length, accessibility); -} - -bool TryRecommitSystemPagesInternal( - uintptr_t address, - size_t length, - PageAccessibilityConfiguration accessibility, - PageAccessibilityDisposition accessibility_disposition) { - // Ignore accessibility_disposition, because decommitting is equivalent to - // making pages inaccessible. - return TrySetSystemPagesAccess(address, length, accessibility); -} - -void DiscardSystemPagesInternal(uintptr_t address, size_t length) { - void* ptr = reinterpret_cast<void*>(address); - // Use DiscardVirtualMemory when available because it releases faster than - // MEM_RESET. - DWORD ret = DiscardVirtualMemory(ptr, length); - // DiscardVirtualMemory is buggy in Win10 SP0, so fall back to MEM_RESET on - // failure. - if (ret) { - PA_CHECK(VirtualAllocWithRetry(ptr, length, MEM_RESET, PAGE_READWRITE)); - } -} - -} // namespace partition_alloc::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_WIN_H_
diff --git a/base/allocator/partition_allocator/page_allocator_unittest.cc b/base/allocator/partition_allocator/page_allocator_unittest.cc deleted file mode 100644 index 8fef6e6..0000000 --- a/base/allocator/partition_allocator/page_allocator_unittest.cc +++ /dev/null
@@ -1,627 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/page_allocator.h" - -#include <stdlib.h> -#include <string.h> - -#include <algorithm> -#include <cstdint> -#include <string> -#include <vector> - -#include "base/allocator/partition_allocator/address_space_randomization.h" -#include "base/allocator/partition_allocator/partition_alloc_base/cpu.h" -#include "base/allocator/partition_allocator/partition_alloc_base/logging.h" -#include "base/allocator/partition_allocator/partition_alloc_config.h" -#include "base/allocator/partition_allocator/partition_alloc_notreached.h" -#include "base/allocator/partition_allocator/tagging.h" -#include "build/build_config.h" - -#if BUILDFLAG(IS_ANDROID) -#include "base/debug/proc_maps_linux.h" -#endif // BUILDFLAG(IS_ANDROID) -#include "testing/gtest/include/gtest/gtest.h" - -#if BUILDFLAG(IS_POSIX) -#include <setjmp.h> -#include <signal.h> -#include <sys/mman.h> -#include <sys/time.h> -#endif // BUILDFLAG(IS_POSIX) - -#include "base/allocator/partition_allocator/arm_bti_test_functions.h" - -#if PA_CONFIG(HAS_MEMORY_TAGGING) -#include <arm_acle.h> -#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) -#define MTE_KILLED_BY_SIGNAL_AVAILABLE -#endif -#endif // PA_CONFIG(HAS_MEMORY_TAGGING) - -#if !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - -namespace partition_alloc::internal { - -#if BUILDFLAG(IS_ANDROID) -namespace base::debug { - -using ::base::debug::MappedMemoryRegion; -using ::base::debug::ParseProcMaps; -using ::base::debug::ReadProcMaps; - -} // namespace base::debug -#endif - -namespace { - -// Any number of bytes that can be allocated with no trouble. -size_t EasyAllocSize() { - return (1024 * 1024) & ~(PageAllocationGranularity() - 1); -} - -// A huge amount of memory, greater than or equal to the ASLR space. -size_t HugeMemoryAmount() { - return std::max(::partition_alloc::internal::ASLRMask(), - std::size_t{2} * ::partition_alloc::internal::ASLRMask()); -} - -} // namespace - -TEST(PartitionAllocPageAllocatorTest, Rounding) { - EXPECT_EQ(0u, RoundUpToSystemPage(0u)); - EXPECT_EQ(SystemPageSize(), RoundUpToSystemPage(1)); - EXPECT_EQ(SystemPageSize(), RoundUpToSystemPage(SystemPageSize() - 1)); - EXPECT_EQ(SystemPageSize(), RoundUpToSystemPage(SystemPageSize())); - EXPECT_EQ(2 * SystemPageSize(), RoundUpToSystemPage(SystemPageSize() + 1)); - EXPECT_EQ(0u, RoundDownToSystemPage(0u)); - EXPECT_EQ(0u, RoundDownToSystemPage(SystemPageSize() - 1)); - EXPECT_EQ(SystemPageSize(), RoundDownToSystemPage(SystemPageSize())); - EXPECT_EQ(SystemPageSize(), RoundDownToSystemPage(SystemPageSize() + 1)); - EXPECT_EQ(SystemPageSize(), RoundDownToSystemPage(2 * SystemPageSize() - 1)); - EXPECT_EQ(0u, RoundUpToPageAllocationGranularity(0u)); - EXPECT_EQ(PageAllocationGranularity(), RoundUpToPageAllocationGranularity(1)); - EXPECT_EQ(PageAllocationGranularity(), RoundUpToPageAllocationGranularity( - PageAllocationGranularity() - 1)); - EXPECT_EQ(PageAllocationGranularity(), - RoundUpToPageAllocationGranularity(PageAllocationGranularity())); - EXPECT_EQ( - 2 * PageAllocationGranularity(), - RoundUpToPageAllocationGranularity(PageAllocationGranularity() + 1)); - EXPECT_EQ(0u, RoundDownToPageAllocationGranularity(0u)); - EXPECT_EQ(0u, RoundDownToPageAllocationGranularity( - PageAllocationGranularity() - 1)); - EXPECT_EQ(PageAllocationGranularity(), - RoundDownToPageAllocationGranularity(PageAllocationGranularity())); - EXPECT_EQ(PageAllocationGranularity(), RoundDownToPageAllocationGranularity( - PageAllocationGranularity() + 1)); - EXPECT_EQ(PageAllocationGranularity(), - RoundDownToPageAllocationGranularity( - 2 * PageAllocationGranularity() - 1)); -} - -TEST(PartitionAllocPageAllocatorTest, NextAlignedWithOffset) { - EXPECT_EQ(1024u, NextAlignedWithOffset(1024, 1, 0)); - EXPECT_EQ(2024u, NextAlignedWithOffset(1024, 1024, 1000)); - EXPECT_EQ(2024u, NextAlignedWithOffset(2024, 1024, 1000)); - EXPECT_EQ(3048u, NextAlignedWithOffset(2025, 1024, 1000)); - EXPECT_EQ(2048u, NextAlignedWithOffset(1024, 2048, 0)); - EXPECT_EQ(2148u, NextAlignedWithOffset(1024, 2048, 100)); - EXPECT_EQ(2000u, NextAlignedWithOffset(1024, 2048, 2000)); -} - -// Test that failed page allocations invoke base::ReleaseReservation(). -// We detect this by making a reservation and ensuring that after failure, we -// can make a new reservation. -TEST(PartitionAllocPageAllocatorTest, AllocFailure) { - // Release any reservation made by another test. - ReleaseReservation(); - - // We can make a reservation. - EXPECT_TRUE(ReserveAddressSpace(EasyAllocSize())); - - // We can't make another reservation until we trigger an allocation failure. - EXPECT_FALSE(ReserveAddressSpace(EasyAllocSize())); - - size_t size = HugeMemoryAmount(); - // Skip the test for sanitizers and platforms with ASLR turned off. - if (size == 0) { - return; - } - - uintptr_t result = - AllocPages(size, PageAllocationGranularity(), - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kInaccessible), - PageTag::kChromium); - if (!result) { - // We triggered allocation failure. Our reservation should have been - // released, and we should be able to make a new reservation. - EXPECT_TRUE(ReserveAddressSpace(EasyAllocSize())); - ReleaseReservation(); - return; - } - // We couldn't fail. Make sure reservation is still there. - EXPECT_FALSE(ReserveAddressSpace(EasyAllocSize())); -} - -// TODO(crbug.com/765801): Test failed on chromium.win/Win10 Tests x64. -#if BUILDFLAG(IS_WIN) && defined(ARCH_CPU_64_BITS) -#define MAYBE_ReserveAddressSpace DISABLED_ReserveAddressSpace -#else -#define MAYBE_ReserveAddressSpace ReserveAddressSpace -#endif // BUILDFLAG(IS_WIN) && defined(ARCH_CPU_64_BITS) - -// Test that reserving address space can fail. -TEST(PartitionAllocPageAllocatorTest, MAYBE_ReserveAddressSpace) { - // Release any reservation made by another test. - ReleaseReservation(); - - size_t size = HugeMemoryAmount(); - // Skip the test for sanitizers and platforms with ASLR turned off. - if (size == 0) { - return; - } - - bool success = ReserveAddressSpace(size); - if (!success) { - EXPECT_TRUE(ReserveAddressSpace(EasyAllocSize())); - return; - } - // We couldn't fail. Make sure reservation is still there. - EXPECT_FALSE(ReserveAddressSpace(EasyAllocSize())); -} - -TEST(PartitionAllocPageAllocatorTest, AllocAndFreePages) { - uintptr_t buffer = - AllocPages(PageAllocationGranularity(), PageAllocationGranularity(), - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kReadWrite), - PageTag::kChromium); - EXPECT_TRUE(buffer); - int* buffer0 = reinterpret_cast<int*>(buffer); - *buffer0 = 42; - EXPECT_EQ(42, *buffer0); - FreePages(buffer, PageAllocationGranularity()); -} - -TEST(PartitionAllocPageAllocatorTest, AllocPagesAligned) { - size_t alignment = 8 * PageAllocationGranularity(); - size_t sizes[] = {PageAllocationGranularity(), - alignment - PageAllocationGranularity(), alignment, - alignment + PageAllocationGranularity(), alignment * 4}; - size_t offsets[] = {0, PageAllocationGranularity(), alignment / 2, - alignment - PageAllocationGranularity()}; - for (size_t size : sizes) { - for (size_t offset : offsets) { - uintptr_t buffer = AllocPagesWithAlignOffset( - 0, size, alignment, offset, - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kReadWrite), - PageTag::kChromium); - EXPECT_TRUE(buffer); - EXPECT_EQ(buffer % alignment, offset); - FreePages(buffer, size); - } - } -} - -TEST(PartitionAllocPageAllocatorTest, - AllocAndFreePagesWithPageReadWriteTagged) { - // This test checks that a page allocated with - // PageAccessibilityConfiguration::kReadWriteTagged is safe to use on all - // systems (even those which don't support MTE). - uintptr_t buffer = - AllocPages(PageAllocationGranularity(), PageAllocationGranularity(), - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kReadWriteTagged), - PageTag::kChromium); - EXPECT_TRUE(buffer); - int* buffer0 = reinterpret_cast<int*>(buffer); - *buffer0 = 42; - EXPECT_EQ(42, *buffer0); - FreePages(buffer, PageAllocationGranularity()); -} - -TEST(PartitionAllocPageAllocatorTest, - AllocAndFreePagesWithPageReadExecuteConfirmCFI) { - // This test checks that indirect branches to anything other than a valid - // branch target in a PageAccessibilityConfiguration::kReadExecute-mapped - // crash on systems which support the Armv8.5 Branch Target Identification - // extension. - base::CPU cpu; - if (!cpu.has_bti()) { -#if BUILDFLAG(IS_IOS) - // Workaround for incorrectly failed iOS tests with GTEST_SKIP, - // see crbug.com/912138 for details. - return; -#else - GTEST_SKIP(); -#endif - } -#if defined(MTE_KILLED_BY_SIGNAL_AVAILABLE) - // Next, map some read-write memory and copy the BTI-enabled function there. - uintptr_t buffer = - AllocPages(PageAllocationGranularity(), PageAllocationGranularity(), - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kReadWrite), - PageTag::kChromium); - ptrdiff_t function_range = - reinterpret_cast<char*>(arm_bti_test_function_end) - - reinterpret_cast<char*>(arm_bti_test_function); - ptrdiff_t invalid_offset = - reinterpret_cast<char*>(arm_bti_test_function_invalid_offset) - - reinterpret_cast<char*>(arm_bti_test_function); - memcpy(reinterpret_cast<void*>(buffer), - reinterpret_cast<void*>(arm_bti_test_function), function_range); - - // Next re-protect the page. - SetSystemPagesAccess( - buffer, PageAllocationGranularity(), - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kReadExecuteProtected)); - - using BTITestFunction = int64_t (*)(int64_t); - - // Attempt to call the function through the BTI-enabled entrypoint. Confirm - // that it works. - BTITestFunction bti_enabled_fn = reinterpret_cast<BTITestFunction>(buffer); - BTITestFunction bti_invalid_fn = - reinterpret_cast<BTITestFunction>(buffer + invalid_offset); - EXPECT_EQ(bti_enabled_fn(15), 18); - // Next, attempt to call the function without the entrypoint. - EXPECT_EXIT({ bti_invalid_fn(15); }, testing::KilledBySignal(SIGILL), - ""); // Should crash with SIGILL. - FreePages(buffer, PageAllocationGranularity()); -#else - PA_NOTREACHED(); -#endif -} - -TEST(PartitionAllocPageAllocatorTest, - AllocAndFreePagesWithPageReadWriteTaggedSynchronous) { - // This test checks that a page allocated with - // PageAccessibilityConfiguration::kReadWriteTagged generates tag violations - // if allocated on a system which supports the - // Armv8.5 Memory Tagging Extension. - base::CPU cpu; - if (!cpu.has_mte()) { - // Skip this test if there's no MTE. -#if BUILDFLAG(IS_IOS) - return; -#else - GTEST_SKIP(); -#endif - } - -#if defined(MTE_KILLED_BY_SIGNAL_AVAILABLE) - uintptr_t buffer = - AllocPages(PageAllocationGranularity(), PageAllocationGranularity(), - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kReadWriteTagged), - PageTag::kChromium); - EXPECT_TRUE(buffer); - int* buffer0 = reinterpret_cast<int*>(buffer); - // Assign an 0x1 tag to the first granule of buffer. - int* buffer1 = __arm_mte_increment_tag(buffer0, 0x1); - EXPECT_NE(buffer0, buffer1); - __arm_mte_set_tag(buffer1); - // Retrieve the tag to ensure that it's set. - buffer1 = __arm_mte_get_tag(buffer0); - // Prove that the tag is different (if they're the same, the test won't work). - ASSERT_NE(buffer0, buffer1); - TagViolationReportingMode parent_tagging_mode = - GetMemoryTaggingModeForCurrentThread(); - EXPECT_EXIT( - { - // Switch to synchronous mode. -#if BUILDFLAG(IS_ANDROID) - ChangeMemoryTaggingModeForAllThreadsPerProcess( - TagViolationReportingMode::kSynchronous); -#else - ChangeMemoryTaggingModeForCurrentThread( - TagViolationReportingMode::kSynchronous); -#endif // BUILDFLAG(IS_ANDROID) - EXPECT_EQ(GetMemoryTaggingModeForCurrentThread(), - TagViolationReportingMode::kSynchronous); - // Write to the buffer using its previous tag. A segmentation fault - // should be delivered. - *buffer0 = 42; - }, - testing::KilledBySignal(SIGSEGV), ""); - EXPECT_EQ(GetMemoryTaggingModeForCurrentThread(), parent_tagging_mode); - FreePages(buffer, PageAllocationGranularity()); -#else - PA_NOTREACHED(); -#endif -} - -TEST(PartitionAllocPageAllocatorTest, - AllocAndFreePagesWithPageReadWriteTaggedAsynchronous) { - // This test checks that a page allocated with - // PageAccessibilityConfiguration::kReadWriteTagged generates tag violations - // if allocated on a system which supports MTE. - base::CPU cpu; - if (!cpu.has_mte()) { - // Skip this test if there's no MTE. -#if BUILDFLAG(IS_IOS) - return; -#else - GTEST_SKIP(); -#endif - } - -#if defined(MTE_KILLED_BY_SIGNAL_AVAILABLE) - uintptr_t buffer = - AllocPages(PageAllocationGranularity(), PageAllocationGranularity(), - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kReadWriteTagged), - PageTag::kChromium); - EXPECT_TRUE(buffer); - int* buffer0 = reinterpret_cast<int*>(buffer); - __arm_mte_set_tag(__arm_mte_increment_tag(buffer0, 0x1)); - int* buffer1 = __arm_mte_get_tag(buffer0); - EXPECT_NE(buffer0, buffer1); - TagViolationReportingMode parent_tagging_mode = - GetMemoryTaggingModeForCurrentThread(); - EXPECT_EXIT( - { - // Switch to asynchronous mode. -#if BUILDFLAG(IS_ANDROID) - ChangeMemoryTaggingModeForAllThreadsPerProcess( - TagViolationReportingMode::kAsynchronous); -#else - ChangeMemoryTaggingModeForCurrentThread( - TagViolationReportingMode::kAsynchronous); -#endif // BUILDFLAG(IS_ANDROID) - EXPECT_EQ(GetMemoryTaggingModeForCurrentThread(), - TagViolationReportingMode::kAsynchronous); - // Write to the buffer using its previous tag. A fault should be - // generated at this point but we may not notice straight away... - *buffer0 = 42; - EXPECT_EQ(42, *buffer0); - PA_LOG(ERROR) << "="; // Until we receive control back from the kernel - // (e.g. on a system call). - }, - testing::KilledBySignal(SIGSEGV), ""); - FreePages(buffer, PageAllocationGranularity()); - EXPECT_EQ(GetMemoryTaggingModeForCurrentThread(), parent_tagging_mode); -#else - PA_NOTREACHED(); -#endif -} - -// Test permission setting on POSIX, where we can set a trap handler. -#if BUILDFLAG(IS_POSIX) - -namespace { -sigjmp_buf g_continuation; - -void SignalHandler(int signal, siginfo_t* info, void*) { - siglongjmp(g_continuation, 1); -} -} // namespace - -// On Mac, sometimes we get SIGBUS instead of SIGSEGV, so handle that too. -#if BUILDFLAG(IS_APPLE) -#define EXTRA_FAULT_BEGIN_ACTION() \ - struct sigaction old_bus_action; \ - sigaction(SIGBUS, &action, &old_bus_action); -#define EXTRA_FAULT_END_ACTION() sigaction(SIGBUS, &old_bus_action, nullptr); -#else -#define EXTRA_FAULT_BEGIN_ACTION() -#define EXTRA_FAULT_END_ACTION() -#endif - -// Install a signal handler so we can catch the fault we're about to trigger. -#define FAULT_TEST_BEGIN() \ - struct sigaction action = {}; \ - struct sigaction old_action = {}; \ - action.sa_sigaction = SignalHandler; \ - sigemptyset(&action.sa_mask); \ - action.sa_flags = SA_SIGINFO; \ - sigaction(SIGSEGV, &action, &old_action); \ - EXTRA_FAULT_BEGIN_ACTION(); \ - int const save_sigs = 1; \ - if (!sigsetjmp(g_continuation, save_sigs)) { -// Fault generating code goes here... - -// Handle when sigsetjmp returns nonzero (we are returning from our handler). -#define FAULT_TEST_END() \ - } \ - else { \ - sigaction(SIGSEGV, &old_action, nullptr); \ - EXTRA_FAULT_END_ACTION(); \ - } - -TEST(PartitionAllocPageAllocatorTest, InaccessiblePages) { - uintptr_t buffer = - AllocPages(PageAllocationGranularity(), PageAllocationGranularity(), - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kInaccessible), - PageTag::kChromium); - EXPECT_TRUE(buffer); - - FAULT_TEST_BEGIN() - - // Reading from buffer should fault. - int* buffer0 = reinterpret_cast<int*>(buffer); - int buffer0_contents = *buffer0; - EXPECT_EQ(buffer0_contents, *buffer0); - EXPECT_TRUE(false); - - FAULT_TEST_END() - - FreePages(buffer, PageAllocationGranularity()); -} - -// TODO(crbug.com/1291888): Understand why we can't read from Read-Execute pages -// on iOS. -#if BUILDFLAG(IS_IOS) -#define MAYBE_ReadExecutePages DISABLED_ReadExecutePages -#else -#define MAYBE_ReadExecutePages ReadExecutePages -#endif // BUILDFLAG(IS_IOS) -TEST(PartitionAllocPageAllocatorTest, MAYBE_ReadExecutePages) { - uintptr_t buffer = - AllocPages(PageAllocationGranularity(), PageAllocationGranularity(), - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kReadExecute), - PageTag::kChromium); - EXPECT_TRUE(buffer); - int* buffer0 = reinterpret_cast<int*>(buffer); - // Reading from buffer should succeed. - int buffer0_contents = *buffer0; - - FAULT_TEST_BEGIN() - - // Writing to buffer should fault. - *buffer0 = ~buffer0_contents; - EXPECT_TRUE(false); - - FAULT_TEST_END() - - // Make sure no write occurred. - EXPECT_EQ(buffer0_contents, *buffer0); - FreePages(buffer, PageAllocationGranularity()); -} - -#endif // BUILDFLAG(IS_POSIX) - -#if BUILDFLAG(IS_ANDROID) -TEST(PartitionAllocPageAllocatorTest, PageTagging) { - uintptr_t buffer = - AllocPages(PageAllocationGranularity(), PageAllocationGranularity(), - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kInaccessible), - PageTag::kChromium); - EXPECT_TRUE(buffer); - - std::string proc_maps; - EXPECT_TRUE(base::debug::ReadProcMaps(&proc_maps)); - std::vector<base::debug::MappedMemoryRegion> regions; - EXPECT_TRUE(base::debug::ParseProcMaps(proc_maps, ®ions)); - - bool found = false; - for (const auto& region : regions) { - if (region.start == buffer) { - found = true; - EXPECT_EQ("[anon:chromium]", region.path); - break; - } - } - - FreePages(buffer, PageAllocationGranularity()); - EXPECT_TRUE(found); -} -#endif // BUILDFLAG(IS_ANDROID) - -TEST(PartitionAllocPageAllocatorTest, DecommitErasesMemory) { - if (!DecommittedMemoryIsAlwaysZeroed()) { - return; - } - - size_t size = PageAllocationGranularity(); - uintptr_t buffer = AllocPages(size, PageAllocationGranularity(), - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kReadWrite), - PageTag::kChromium); - ASSERT_TRUE(buffer); - - memset(reinterpret_cast<void*>(buffer), 42, size); - - DecommitSystemPages(buffer, size, - PageAccessibilityDisposition::kAllowKeepForPerf); - RecommitSystemPages(buffer, size, - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kReadWrite), - PageAccessibilityDisposition::kAllowKeepForPerf); - - uint8_t* recommitted_buffer = reinterpret_cast<uint8_t*>(buffer); - uint32_t sum = 0; - for (size_t i = 0; i < size; i++) { - sum += recommitted_buffer[i]; - } - EXPECT_EQ(0u, sum) << "Data was not erased"; - - FreePages(buffer, size); -} - -TEST(PartitionAllocPageAllocatorTest, DecommitAndZero) { - size_t size = PageAllocationGranularity(); - uintptr_t buffer = AllocPages(size, PageAllocationGranularity(), - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kReadWrite), - PageTag::kChromium); - ASSERT_TRUE(buffer); - - memset(reinterpret_cast<void*>(buffer), 42, size); - - DecommitAndZeroSystemPages(buffer, size); - -// Test permission setting on POSIX, where we can set a trap handler. -#if BUILDFLAG(IS_POSIX) - - FAULT_TEST_BEGIN() - - // Reading from buffer should now fault. - int* buffer0 = reinterpret_cast<int*>(buffer); - int buffer0_contents = *buffer0; - EXPECT_EQ(buffer0_contents, *buffer0); - EXPECT_TRUE(false); - - FAULT_TEST_END() - -#endif - - // Clients of the DecommitAndZero API (in particular, V8), currently just - // call SetSystemPagesAccess to mark the region as accessible again, so we - // use that here as well. - SetSystemPagesAccess(buffer, size, - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kReadWrite)); - - uint8_t* recommitted_buffer = reinterpret_cast<uint8_t*>(buffer); - uint32_t sum = 0; - for (size_t i = 0; i < size; i++) { - sum += recommitted_buffer[i]; - } - EXPECT_EQ(0u, sum) << "Data was not erased"; - - FreePages(buffer, size); -} - -TEST(PartitionAllocPageAllocatorTest, MappedPagesAccounting) { - size_t size = PageAllocationGranularity(); - // Ask for a large alignment to make sure that trimming doesn't change the - // accounting. - size_t alignment = 128 * PageAllocationGranularity(); - size_t offsets[] = {0, PageAllocationGranularity(), alignment / 2, - alignment - PageAllocationGranularity()}; - - size_t mapped_size_before = GetTotalMappedSize(); - - for (size_t offset : offsets) { - uintptr_t data = AllocPagesWithAlignOffset( - 0, size, alignment, offset, - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kInaccessible), - PageTag::kChromium); - ASSERT_TRUE(data); - - EXPECT_EQ(mapped_size_before + size, GetTotalMappedSize()); - - DecommitSystemPages(data, size, - PageAccessibilityDisposition::kAllowKeepForPerf); - EXPECT_EQ(mapped_size_before + size, GetTotalMappedSize()); - - FreePages(data, size); - EXPECT_EQ(mapped_size_before, GetTotalMappedSize()); - } -} - -} // namespace partition_alloc::internal - -#endif // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
diff --git a/base/allocator/partition_allocator/partition_address_space.cc b/base/allocator/partition_allocator/partition_address_space.cc deleted file mode 100644 index 760a1ff..0000000 --- a/base/allocator/partition_allocator/partition_address_space.cc +++ /dev/null
@@ -1,428 +0,0 @@ -// 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. - -#include "base/allocator/partition_allocator/partition_address_space.h" - -#include <array> -#include <cstddef> -#include <cstdint> -#include <ostream> -#include <string> - -#include "base/allocator/partition_allocator/address_pool_manager.h" -#include "base/allocator/partition_allocator/compressed_pointer.h" -#include "base/allocator/partition_allocator/page_allocator.h" -#include "base/allocator/partition_allocator/partition_alloc_base/bits.h" -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/alias.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/partition_alloc_config.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "base/allocator/partition_allocator/pkey.h" -#include "build/build_config.h" - -#if BUILDFLAG(IS_IOS) -#include <mach-o/dyld.h> -#endif - -#if BUILDFLAG(IS_WIN) -#include <windows.h> -#endif // BUILDFLAG(IS_WIN) - -#if PA_CONFIG(ENABLE_SHADOW_METADATA) || BUILDFLAG(ENABLE_PKEYS) -#include <sys/mman.h> -#endif - -namespace partition_alloc::internal { - -#if BUILDFLAG(HAS_64_BIT_POINTERS) - -namespace { - -#if BUILDFLAG(IS_WIN) - -PA_NOINLINE void HandlePoolAllocFailureOutOfVASpace() { - PA_NO_CODE_FOLDING(); - PA_CHECK(false); -} - -PA_NOINLINE void HandlePoolAllocFailureOutOfCommitCharge() { - PA_NO_CODE_FOLDING(); - PA_CHECK(false); -} -#endif // BUILDFLAG(IS_WIN) - -PA_NOINLINE void HandlePoolAllocFailure() { - PA_NO_CODE_FOLDING(); - uint32_t alloc_page_error_code = GetAllocPageErrorCode(); - PA_DEBUG_DATA_ON_STACK("error", static_cast<size_t>(alloc_page_error_code)); - // It's important to easily differentiate these two failures on Windows, so - // crash with different stacks. -#if BUILDFLAG(IS_WIN) - if (alloc_page_error_code == ERROR_NOT_ENOUGH_MEMORY) { - // The error code says NOT_ENOUGH_MEMORY, but since we only do MEM_RESERVE, - // it must be VA space exhaustion. - HandlePoolAllocFailureOutOfVASpace(); - } else if (alloc_page_error_code == ERROR_COMMITMENT_LIMIT) { - // Should not happen, since as of Windows 8.1+, reserving address space - // should not be charged against the commit limit, aside from a very small - // amount per 64kiB block. Keep this path anyway, to check in crash reports. - HandlePoolAllocFailureOutOfCommitCharge(); - } else -#endif // BUILDFLAG(IS_WIN) - { - PA_CHECK(false); - } -} - -} // namespace - -#if BUILDFLAG(ENABLE_PKEYS) -alignas(PA_PKEY_ALIGN_SZ) -#else -alignas(kPartitionCachelineSize) -#endif - PartitionAddressSpace::PoolSetup PartitionAddressSpace::setup_; - -#if PA_CONFIG(ENABLE_SHADOW_METADATA) -std::ptrdiff_t PartitionAddressSpace::regular_pool_shadow_offset_ = 0; -std::ptrdiff_t PartitionAddressSpace::brp_pool_shadow_offset_ = 0; -#endif - -#if PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE) -#if !BUILDFLAG(IS_IOS) -#error Dynamic pool size is only supported on iOS. -#endif - -namespace { -bool IsIOSTestProcess() { - // On iOS, only applications with the extended virtual addressing entitlement - // can use a large address space. Since Earl Grey test runner apps cannot get - // entitlements, they must use a much smaller pool size. Similarly, - // integration tests for ChromeWebView end up with two PartitionRoots since - // both the integration tests and ChromeWebView have a copy of base/. Even - // with the entitlement, there is insufficient address space for two - // PartitionRoots, so a smaller pool size is needed. - - // Use a fixed buffer size to avoid allocation inside the allocator. - constexpr size_t path_buffer_size = 8192; - char executable_path[path_buffer_size]; - - uint32_t executable_length = path_buffer_size; - int rv = _NSGetExecutablePath(executable_path, &executable_length); - PA_CHECK(!rv); - size_t executable_path_length = - std::char_traits<char>::length(executable_path); - - auto has_suffix = [&](const char* suffix) -> bool { - size_t suffix_length = std::char_traits<char>::length(suffix); - if (executable_path_length < suffix_length) { - return false; - } - return std::char_traits<char>::compare( - executable_path + (executable_path_length - suffix_length), - suffix, suffix_length) == 0; - }; - - return has_suffix("Runner") || has_suffix("ios_web_view_inttests"); -} -} // namespace - -PA_ALWAYS_INLINE size_t PartitionAddressSpace::RegularPoolSize() { - return IsIOSTestProcess() ? kRegularPoolSizeForIOSTestProcess - : kRegularPoolSize; -} -PA_ALWAYS_INLINE size_t PartitionAddressSpace::BRPPoolSize() { - return IsIOSTestProcess() ? kBRPPoolSizeForIOSTestProcess : kBRPPoolSize; -} -#endif // PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE) - -void PartitionAddressSpace::Init() { - if (IsInitialized()) { - return; - } - - size_t regular_pool_size = RegularPoolSize(); - size_t brp_pool_size = BRPPoolSize(); - -#if PA_CONFIG(GLUE_CORE_POOLS) - // Gluing core pools (regular & BRP) makes sense only when both pools are of - // the same size. This the only way we can check belonging to either of the - // two with a single bitmask operation. - PA_CHECK(regular_pool_size == brp_pool_size); - - // TODO(crbug.com/1362969): Support PA_ENABLE_SHADOW_METADATA. - int pools_fd = -1; - - size_t glued_pool_sizes = regular_pool_size * 2; - // Note, BRP pool requires to be preceded by a "forbidden zone", which is - // conveniently taken care of by the last guard page of the regular pool. - setup_.regular_pool_base_address_ = - AllocPages(glued_pool_sizes, glued_pool_sizes, - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kInaccessible), - PageTag::kPartitionAlloc, pools_fd); - if (!setup_.regular_pool_base_address_) { - HandlePoolAllocFailure(); - } - setup_.brp_pool_base_address_ = - setup_.regular_pool_base_address_ + regular_pool_size; -#else // PA_CONFIG(GLUE_CORE_POOLS) -#if PA_CONFIG(ENABLE_SHADOW_METADATA) - int regular_pool_fd = memfd_create("/regular_pool", MFD_CLOEXEC); -#else - int regular_pool_fd = -1; -#endif - setup_.regular_pool_base_address_ = - AllocPages(regular_pool_size, regular_pool_size, - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kInaccessible), - PageTag::kPartitionAlloc, regular_pool_fd); - if (!setup_.regular_pool_base_address_) { - HandlePoolAllocFailure(); - } - -#if PA_CONFIG(ENABLE_SHADOW_METADATA) - int brp_pool_fd = memfd_create("/brp_pool", MFD_CLOEXEC); -#else - int brp_pool_fd = -1; -#endif - // Reserve an extra allocation granularity unit before the BRP pool, but keep - // the pool aligned at BRPPoolSize(). A pointer immediately past an allocation - // is a valid pointer, and having a "forbidden zone" before the BRP pool - // prevents such a pointer from "sneaking into" the pool. - const size_t kForbiddenZoneSize = PageAllocationGranularity(); - uintptr_t base_address = AllocPagesWithAlignOffset( - 0, brp_pool_size + kForbiddenZoneSize, brp_pool_size, - brp_pool_size - kForbiddenZoneSize, - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kInaccessible), - PageTag::kPartitionAlloc, brp_pool_fd); - if (!base_address) { - HandlePoolAllocFailure(); - } - setup_.brp_pool_base_address_ = base_address + kForbiddenZoneSize; -#endif // PA_CONFIG(GLUE_CORE_POOLS) - -#if PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE) - setup_.regular_pool_base_mask_ = ~(regular_pool_size - 1); - setup_.brp_pool_base_mask_ = ~(brp_pool_size - 1); -#if PA_CONFIG(GLUE_CORE_POOLS) - // When PA_GLUE_CORE_POOLS is on, the BRP pool is placed at the end of the - // regular pool, effectively forming one virtual pool of a twice bigger - // size. Adjust the mask appropriately. - setup_.core_pools_base_mask_ = setup_.regular_pool_base_mask_ << 1; - PA_DCHECK(setup_.core_pools_base_mask_ == (setup_.brp_pool_base_mask_ << 1)); -#endif -#endif // PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE) - - AddressPoolManager::GetInstance().Add( - kRegularPoolHandle, setup_.regular_pool_base_address_, regular_pool_size); - AddressPoolManager::GetInstance().Add( - kBRPPoolHandle, setup_.brp_pool_base_address_, brp_pool_size); - - // Sanity check pool alignment. - PA_DCHECK(!(setup_.regular_pool_base_address_ & (regular_pool_size - 1))); - PA_DCHECK(!(setup_.brp_pool_base_address_ & (brp_pool_size - 1))); -#if PA_CONFIG(GLUE_CORE_POOLS) - PA_DCHECK(!(setup_.regular_pool_base_address_ & (glued_pool_sizes - 1))); -#endif - - // Sanity check pool belonging. - PA_DCHECK(!IsInRegularPool(setup_.regular_pool_base_address_ - 1)); - PA_DCHECK(IsInRegularPool(setup_.regular_pool_base_address_)); - PA_DCHECK(IsInRegularPool(setup_.regular_pool_base_address_ + - regular_pool_size - 1)); - PA_DCHECK( - !IsInRegularPool(setup_.regular_pool_base_address_ + regular_pool_size)); - PA_DCHECK(!IsInBRPPool(setup_.brp_pool_base_address_ - 1)); - PA_DCHECK(IsInBRPPool(setup_.brp_pool_base_address_)); - PA_DCHECK(IsInBRPPool(setup_.brp_pool_base_address_ + brp_pool_size - 1)); - PA_DCHECK(!IsInBRPPool(setup_.brp_pool_base_address_ + brp_pool_size)); -#if PA_CONFIG(GLUE_CORE_POOLS) - PA_DCHECK(!IsInCorePools(setup_.regular_pool_base_address_ - 1)); - PA_DCHECK(IsInCorePools(setup_.regular_pool_base_address_)); - PA_DCHECK( - IsInCorePools(setup_.regular_pool_base_address_ + regular_pool_size - 1)); - PA_DCHECK( - IsInCorePools(setup_.regular_pool_base_address_ + regular_pool_size)); - PA_DCHECK(IsInCorePools(setup_.brp_pool_base_address_ - 1)); - PA_DCHECK(IsInCorePools(setup_.brp_pool_base_address_)); - PA_DCHECK(IsInCorePools(setup_.brp_pool_base_address_ + brp_pool_size - 1)); - PA_DCHECK(!IsInCorePools(setup_.brp_pool_base_address_ + brp_pool_size)); -#endif // PA_CONFIG(GLUE_CORE_POOLS) - -#if PA_CONFIG(STARSCAN_USE_CARD_TABLE) - // Reserve memory for PCScan quarantine card table. - uintptr_t requested_address = setup_.regular_pool_base_address_; - uintptr_t actual_address = AddressPoolManager::GetInstance().Reserve( - kRegularPoolHandle, requested_address, kSuperPageSize); - PA_CHECK(requested_address == actual_address) - << "QuarantineCardTable is required to be allocated at the beginning of " - "the regular pool"; -#endif // PA_CONFIG(STARSCAN_USE_CARD_TABLE) - -#if PA_CONFIG(ENABLE_SHADOW_METADATA) - // Reserve memory for the shadow pools. - uintptr_t regular_pool_shadow_address = - AllocPages(regular_pool_size, regular_pool_size, - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kInaccessible), - PageTag::kPartitionAlloc, regular_pool_fd); - regular_pool_shadow_offset_ = - regular_pool_shadow_address - setup_.regular_pool_base_address_; - - uintptr_t brp_pool_shadow_address = AllocPagesWithAlignOffset( - 0, brp_pool_size + kForbiddenZoneSize, brp_pool_size, - brp_pool_size - kForbiddenZoneSize, - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kInaccessible), - PageTag::kPartitionAlloc, brp_pool_fd); - brp_pool_shadow_offset_ = - brp_pool_shadow_address - setup_.brp_pool_base_address_; -#endif - -#if PA_CONFIG(POINTER_COMPRESSION) - CompressedPointerBaseGlobal::SetBase(setup_.regular_pool_base_address_); -#endif // PA_CONFIG(POINTER_COMPRESSION) -} - -void PartitionAddressSpace::InitConfigurablePool(uintptr_t pool_base, - size_t size) { - // The ConfigurablePool must only be initialized once. - PA_CHECK(!IsConfigurablePoolInitialized()); - -#if BUILDFLAG(ENABLE_PKEYS) - // It's possible that the pkey pool has been initialized first, in which case - // the setup_ memory has been made read-only. Remove the protection - // temporarily. - if (IsPkeyPoolInitialized()) { - TagGlobalsWithPkey(kDefaultPkey); - } -#endif - - PA_CHECK(pool_base); - PA_CHECK(size <= kConfigurablePoolMaxSize); - PA_CHECK(size >= kConfigurablePoolMinSize); - PA_CHECK(base::bits::IsPowerOfTwo(size)); - PA_CHECK(pool_base % size == 0); - - setup_.configurable_pool_base_address_ = pool_base; - setup_.configurable_pool_base_mask_ = ~(size - 1); - - AddressPoolManager::GetInstance().Add( - kConfigurablePoolHandle, setup_.configurable_pool_base_address_, size); - -#if BUILDFLAG(ENABLE_PKEYS) - // Put the pkey protection back in place. - if (IsPkeyPoolInitialized()) { - TagGlobalsWithPkey(setup_.pkey_); - } -#endif -} - -#if BUILDFLAG(ENABLE_PKEYS) -void PartitionAddressSpace::InitPkeyPool(int pkey) { - // The PkeyPool can't be initialized with conflicting pkeys. - if (IsPkeyPoolInitialized()) { - PA_CHECK(setup_.pkey_ == pkey); - return; - } - - size_t pool_size = PkeyPoolSize(); - setup_.pkey_pool_base_address_ = - AllocPages(pool_size, pool_size, - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kInaccessible), - PageTag::kPartitionAlloc); - if (!setup_.pkey_pool_base_address_) { - HandlePoolAllocFailure(); - } - - PA_DCHECK(!(setup_.pkey_pool_base_address_ & (pool_size - 1))); - setup_.pkey_ = pkey; - AddressPoolManager::GetInstance().Add( - kPkeyPoolHandle, setup_.pkey_pool_base_address_, pool_size); - - PA_DCHECK(!IsInPkeyPool(setup_.pkey_pool_base_address_ - 1)); - PA_DCHECK(IsInPkeyPool(setup_.pkey_pool_base_address_)); - PA_DCHECK(IsInPkeyPool(setup_.pkey_pool_base_address_ + pool_size - 1)); - PA_DCHECK(!IsInPkeyPool(setup_.pkey_pool_base_address_ + pool_size)); - - // TODO(1362969): support PA_ENABLE_SHADOW_METADATA -} -#endif // BUILDFLAG(ENABLE_PKEYS) - -void PartitionAddressSpace::UninitForTesting() { -#if BUILDFLAG(ENABLE_PKEYS) - UninitPkeyPoolForTesting(); // IN-TEST -#endif -#if PA_CONFIG(GLUE_CORE_POOLS) - // The core pools (regular & BRP) were allocated using a single allocation of - // double size. - FreePages(setup_.regular_pool_base_address_, 2 * RegularPoolSize()); -#else // PA_CONFIG(GLUE_CORE_POOLS) - FreePages(setup_.regular_pool_base_address_, RegularPoolSize()); - // For BRP pool, the allocation region includes a "forbidden zone" before the - // pool. - const size_t kForbiddenZoneSize = PageAllocationGranularity(); - FreePages(setup_.brp_pool_base_address_ - kForbiddenZoneSize, - BRPPoolSize() + kForbiddenZoneSize); -#endif // PA_CONFIG(GLUE_CORE_POOLS) - // Do not free pages for the configurable pool, because its memory is owned - // by someone else, but deinitialize it nonetheless. - setup_.regular_pool_base_address_ = kUninitializedPoolBaseAddress; - setup_.brp_pool_base_address_ = kUninitializedPoolBaseAddress; - setup_.configurable_pool_base_address_ = kUninitializedPoolBaseAddress; - setup_.configurable_pool_base_mask_ = 0; - AddressPoolManager::GetInstance().ResetForTesting(); -#if PA_CONFIG(POINTER_COMPRESSION) - CompressedPointerBaseGlobal::ResetBaseForTesting(); -#endif // PA_CONFIG(POINTER_COMPRESSION) -} - -void PartitionAddressSpace::UninitConfigurablePoolForTesting() { -#if BUILDFLAG(ENABLE_PKEYS) - // It's possible that the pkey pool has been initialized first, in which case - // the setup_ memory has been made read-only. Remove the protection - // temporarily. - if (IsPkeyPoolInitialized()) { - TagGlobalsWithPkey(kDefaultPkey); - } -#endif - AddressPoolManager::GetInstance().Remove(kConfigurablePoolHandle); - setup_.configurable_pool_base_address_ = kUninitializedPoolBaseAddress; - setup_.configurable_pool_base_mask_ = 0; -#if BUILDFLAG(ENABLE_PKEYS) - // Put the pkey protection back in place. - if (IsPkeyPoolInitialized()) { - TagGlobalsWithPkey(setup_.pkey_); - } -#endif -} - -#if BUILDFLAG(ENABLE_PKEYS) -void PartitionAddressSpace::UninitPkeyPoolForTesting() { - if (IsPkeyPoolInitialized()) { - TagGlobalsWithPkey(kDefaultPkey); - PkeySettings::settings.enabled = false; - - FreePages(setup_.pkey_pool_base_address_, PkeyPoolSize()); - AddressPoolManager::GetInstance().Remove(kPkeyPoolHandle); - setup_.pkey_pool_base_address_ = kUninitializedPoolBaseAddress; - setup_.pkey_ = kInvalidPkey; - } -} -#endif - -#if BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64) - -PageCharacteristics page_characteristics; - -#endif // BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64) - -#endif // BUILDFLAG(HAS_64_BIT_POINTERS) - -} // namespace partition_alloc::internal
diff --git a/base/allocator/partition_allocator/partition_address_space.h b/base/allocator/partition_allocator/partition_address_space.h deleted file mode 100644 index 5e7050e..0000000 --- a/base/allocator/partition_allocator/partition_address_space.h +++ /dev/null
@@ -1,480 +0,0 @@ -// 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 BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ADDRESS_SPACE_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ADDRESS_SPACE_H_ - -#include <algorithm> -#include <array> -#include <cstddef> -#include <limits> - -#include "base/allocator/partition_allocator/address_pool_manager_types.h" -#include "base/allocator/partition_allocator/page_allocator_constants.h" -#include "base/allocator/partition_allocator/partition_alloc_base/bits.h" -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/partition_alloc_config.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "base/allocator/partition_allocator/partition_alloc_forward.h" -#include "base/allocator/partition_allocator/partition_alloc_notreached.h" -#include "base/allocator/partition_allocator/pkey.h" -#include "base/allocator/partition_allocator/tagging.h" -#include "build/build_config.h" - -// The feature is not applicable to 32-bit address space. -#if BUILDFLAG(HAS_64_BIT_POINTERS) - -namespace partition_alloc { - -namespace internal { - -// Manages PartitionAlloc address space, which is split into pools. -// See `glossary.md`. -class PA_COMPONENT_EXPORT(PARTITION_ALLOC) PartitionAddressSpace { - public: -#if PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE) - PA_ALWAYS_INLINE static uintptr_t RegularPoolBaseMask() { - return setup_.regular_pool_base_mask_; - } -#else - PA_ALWAYS_INLINE static constexpr uintptr_t RegularPoolBaseMask() { - return kRegularPoolBaseMask; - } -#endif - - PA_ALWAYS_INLINE static std::pair<pool_handle, uintptr_t> GetPoolAndOffset( - uintptr_t address) { - // When USE_BACKUP_REF_PTR is off, BRP pool isn't used. -#if !BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - PA_DCHECK(!IsInBRPPool(address)); -#endif - pool_handle pool = kNullPoolHandle; - uintptr_t base = 0; - if (IsInRegularPool(address)) { - pool = kRegularPoolHandle; - base = setup_.regular_pool_base_address_; -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - } else if (IsInBRPPool(address)) { - pool = kBRPPoolHandle; - base = setup_.brp_pool_base_address_; -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - } else if (IsInConfigurablePool(address)) { - PA_DCHECK(IsConfigurablePoolInitialized()); - pool = kConfigurablePoolHandle; - base = setup_.configurable_pool_base_address_; -#if BUILDFLAG(ENABLE_PKEYS) - } else if (IsInPkeyPool(address)) { - pool = kPkeyPoolHandle; - base = setup_.pkey_pool_base_address_; -#endif - } else { - PA_NOTREACHED(); - } - return std::make_pair(pool, address - base); - } - PA_ALWAYS_INLINE static constexpr size_t ConfigurablePoolMaxSize() { - return kConfigurablePoolMaxSize; - } - PA_ALWAYS_INLINE static constexpr size_t ConfigurablePoolMinSize() { - return kConfigurablePoolMinSize; - } - - // Initialize pools (except for the configurable one). - // - // This function must only be called from the main thread. - static void Init(); - // Initialize the ConfigurablePool at the given address |pool_base|. It must - // be aligned to the size of the pool. The size must be a power of two and - // must be within [ConfigurablePoolMinSize(), ConfigurablePoolMaxSize()]. - // - // This function must only be called from the main thread. - static void InitConfigurablePool(uintptr_t pool_base, size_t size); -#if BUILDFLAG(ENABLE_PKEYS) - static void InitPkeyPool(int pkey); - static void UninitPkeyPoolForTesting(); -#endif - static void UninitForTesting(); - static void UninitConfigurablePoolForTesting(); - - PA_ALWAYS_INLINE static bool IsInitialized() { - // Either neither or both regular and BRP pool are initialized. The - // configurable and pkey pool are initialized separately. - if (setup_.regular_pool_base_address_ != kUninitializedPoolBaseAddress) { - PA_DCHECK(setup_.brp_pool_base_address_ != kUninitializedPoolBaseAddress); - return true; - } - - PA_DCHECK(setup_.brp_pool_base_address_ == kUninitializedPoolBaseAddress); - return false; - } - - PA_ALWAYS_INLINE static bool IsConfigurablePoolInitialized() { - return setup_.configurable_pool_base_address_ != - kUninitializedPoolBaseAddress; - } - -#if BUILDFLAG(ENABLE_PKEYS) - PA_ALWAYS_INLINE static bool IsPkeyPoolInitialized() { - return setup_.pkey_pool_base_address_ != kUninitializedPoolBaseAddress; - } -#endif - - // Returns false for nullptr. - PA_ALWAYS_INLINE static bool IsInRegularPool(uintptr_t address) { -#if PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE) - const uintptr_t regular_pool_base_mask = setup_.regular_pool_base_mask_; -#else - constexpr uintptr_t regular_pool_base_mask = kRegularPoolBaseMask; -#endif - return (address & regular_pool_base_mask) == - setup_.regular_pool_base_address_; - } - - PA_ALWAYS_INLINE static uintptr_t RegularPoolBase() { - return setup_.regular_pool_base_address_; - } - - // Returns false for nullptr. - PA_ALWAYS_INLINE static bool IsInBRPPool(uintptr_t address) { -#if PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE) - const uintptr_t brp_pool_base_mask = setup_.brp_pool_base_mask_; -#else - constexpr uintptr_t brp_pool_base_mask = kBRPPoolBaseMask; -#endif - return (address & brp_pool_base_mask) == setup_.brp_pool_base_address_; - } - -#if PA_CONFIG(GLUE_CORE_POOLS) - // Checks whether the address belongs to either regular or BRP pool. - // Returns false for nullptr. - PA_ALWAYS_INLINE static bool IsInCorePools(uintptr_t address) { -#if PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE) - const uintptr_t core_pools_base_mask = setup_.core_pools_base_mask_; -#else - // When PA_GLUE_CORE_POOLS is on, the BRP pool is placed at the end of the - // regular pool, effectively forming one virtual pool of a twice bigger - // size. Adjust the mask appropriately. - constexpr uintptr_t core_pools_base_mask = kRegularPoolBaseMask << 1; -#endif // PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE) - bool ret = - (address & core_pools_base_mask) == setup_.regular_pool_base_address_; - PA_DCHECK(ret == (IsInRegularPool(address) || IsInBRPPool(address))); - return ret; - } -#if PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE) - PA_ALWAYS_INLINE static size_t CorePoolsSize() { - return RegularPoolSize() * 2; - } -#else - PA_ALWAYS_INLINE static constexpr size_t CorePoolsSize() { - return RegularPoolSize() * 2; - } -#endif // PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE) -#endif // PA_CONFIG(GLUE_CORE_POOLS) - - PA_ALWAYS_INLINE static uintptr_t OffsetInBRPPool(uintptr_t address) { - PA_DCHECK(IsInBRPPool(address)); - return address - setup_.brp_pool_base_address_; - } - - // Returns false for nullptr. - PA_ALWAYS_INLINE static bool IsInConfigurablePool(uintptr_t address) { - return (address & setup_.configurable_pool_base_mask_) == - setup_.configurable_pool_base_address_; - } - - PA_ALWAYS_INLINE static uintptr_t ConfigurablePoolBase() { - return setup_.configurable_pool_base_address_; - } - -#if BUILDFLAG(ENABLE_PKEYS) - // Returns false for nullptr. - PA_ALWAYS_INLINE static bool IsInPkeyPool(uintptr_t address) { - return (address & kPkeyPoolBaseMask) == setup_.pkey_pool_base_address_; - } -#endif - -#if PA_CONFIG(ENABLE_SHADOW_METADATA) - PA_ALWAYS_INLINE static std::ptrdiff_t ShadowPoolOffset(pool_handle pool) { - if (pool == kRegularPoolHandle) { - return regular_pool_shadow_offset_; - } else if (pool == kBRPPoolHandle) { - return brp_pool_shadow_offset_; - } else { - // TODO(crbug.com/1362969): Add shadow for configurable pool as well. - // Shadow is not created for ConfigurablePool for now, so this part should - // be unreachable. - PA_NOTREACHED(); - return 0; - } - } -#endif - - // PartitionAddressSpace is static_only class. - PartitionAddressSpace() = delete; - PartitionAddressSpace(const PartitionAddressSpace&) = delete; - void* operator new(size_t) = delete; - void* operator new(size_t, void*) = delete; - - private: -#if PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE) - PA_ALWAYS_INLINE static size_t RegularPoolSize(); - PA_ALWAYS_INLINE static size_t BRPPoolSize(); -#else - // The pool sizes should be as large as maximum whenever possible. - PA_ALWAYS_INLINE static constexpr size_t RegularPoolSize() { - return kRegularPoolSize; - } - PA_ALWAYS_INLINE static constexpr size_t BRPPoolSize() { - return kBRPPoolSize; - } -#endif // PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE) - -#if BUILDFLAG(ENABLE_PKEYS) - PA_ALWAYS_INLINE static constexpr size_t PkeyPoolSize() { - return kPkeyPoolSize; - } -#endif - - // On 64-bit systems, PA allocates from several contiguous, mutually disjoint - // pools. The BRP pool is where all allocations have a BRP ref-count, thus - // pointers pointing there can use a BRP protection against UaF. Allocations - // in the other pools don't have that. - // - // Pool sizes have to be the power of two. Each pool will be aligned at its - // own size boundary. - // - // NOTE! The BRP pool must be preceded by an inaccessible region. This is to - // prevent a pointer to the end of a non-BRP-pool allocation from falling into - // the BRP pool, thus triggering BRP mechanism and likely crashing. This - // "forbidden zone" can be as small as 1B, but it's simpler to just reserve an - // allocation granularity unit. - // - // The ConfigurablePool is an optional Pool that can be created inside an - // existing mapping provided by the embedder. This Pool can be used when - // certain PA allocations must be located inside a given virtual address - // region. One use case for this Pool is V8 Sandbox, which requires that - // ArrayBuffers be located inside of it. - static constexpr size_t kRegularPoolSize = kPoolMaxSize; - static constexpr size_t kBRPPoolSize = kPoolMaxSize; - static_assert(base::bits::IsPowerOfTwo(kRegularPoolSize)); - static_assert(base::bits::IsPowerOfTwo(kBRPPoolSize)); -#if BUILDFLAG(ENABLE_PKEYS) - static constexpr size_t kPkeyPoolSize = kGiB / 4; - static_assert(base::bits::IsPowerOfTwo(kPkeyPoolSize)); -#endif - static constexpr size_t kConfigurablePoolMaxSize = kPoolMaxSize; - static constexpr size_t kConfigurablePoolMinSize = 1 * kGiB; - static_assert(kConfigurablePoolMinSize <= kConfigurablePoolMaxSize); - static_assert(base::bits::IsPowerOfTwo(kConfigurablePoolMaxSize)); - static_assert(base::bits::IsPowerOfTwo(kConfigurablePoolMinSize)); - -#if BUILDFLAG(IS_IOS) - -#if !PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE) -#error iOS is only supported with a dynamically sized GigaCase. -#endif - - // We can't afford pool sizes as large as kPoolMaxSize in iOS EarlGrey tests, - // since the test process cannot use an extended virtual address space (see - // crbug.com/1250788). - static constexpr size_t kRegularPoolSizeForIOSTestProcess = kGiB / 4; - static constexpr size_t kBRPPoolSizeForIOSTestProcess = kGiB / 4; - static_assert(kRegularPoolSizeForIOSTestProcess < kRegularPoolSize); - static_assert(kBRPPoolSizeForIOSTestProcess < kBRPPoolSize); - static_assert(base::bits::IsPowerOfTwo(kRegularPoolSizeForIOSTestProcess)); - static_assert(base::bits::IsPowerOfTwo(kBRPPoolSizeForIOSTestProcess)); -#endif // BUILDFLAG(IOS_IOS) - -#if !PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE) - // Masks used to easy determine belonging to a pool. - static constexpr uintptr_t kRegularPoolOffsetMask = - static_cast<uintptr_t>(kRegularPoolSize) - 1; - static constexpr uintptr_t kRegularPoolBaseMask = ~kRegularPoolOffsetMask; - static constexpr uintptr_t kBRPPoolOffsetMask = - static_cast<uintptr_t>(kBRPPoolSize) - 1; - static constexpr uintptr_t kBRPPoolBaseMask = ~kBRPPoolOffsetMask; -#endif // !PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE) - -#if BUILDFLAG(ENABLE_PKEYS) - static constexpr uintptr_t kPkeyPoolOffsetMask = - static_cast<uintptr_t>(kPkeyPoolSize) - 1; - static constexpr uintptr_t kPkeyPoolBaseMask = ~kPkeyPoolOffsetMask; -#endif - - // This must be set to such a value that IsIn*Pool() always returns false when - // the pool isn't initialized. - static constexpr uintptr_t kUninitializedPoolBaseAddress = - static_cast<uintptr_t>(-1); - - struct PoolSetup { - // Before PartitionAddressSpace::Init(), no allocation are allocated from a - // reserved address space. Therefore, set *_pool_base_address_ initially to - // -1, so that PartitionAddressSpace::IsIn*Pool() always returns false. - constexpr PoolSetup() - : regular_pool_base_address_(kUninitializedPoolBaseAddress), - brp_pool_base_address_(kUninitializedPoolBaseAddress), - configurable_pool_base_address_(kUninitializedPoolBaseAddress), -#if BUILDFLAG(ENABLE_PKEYS) - pkey_pool_base_address_(kUninitializedPoolBaseAddress), -#endif -#if PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE) - regular_pool_base_mask_(0), - brp_pool_base_mask_(0), -#if PA_CONFIG(GLUE_CORE_POOLS) - core_pools_base_mask_(0), -#endif -#endif // PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE) - configurable_pool_base_mask_(0) -#if BUILDFLAG(ENABLE_PKEYS) - , - pkey_(kInvalidPkey) -#endif - { - } - - // Using a union to enforce padding. - union { - struct { - uintptr_t regular_pool_base_address_; - uintptr_t brp_pool_base_address_; - uintptr_t configurable_pool_base_address_; -#if BUILDFLAG(ENABLE_PKEYS) - uintptr_t pkey_pool_base_address_; -#endif -#if PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE) - uintptr_t regular_pool_base_mask_; - uintptr_t brp_pool_base_mask_; -#if PA_CONFIG(GLUE_CORE_POOLS) - uintptr_t core_pools_base_mask_; -#endif -#endif // PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE) - uintptr_t configurable_pool_base_mask_; -#if BUILDFLAG(ENABLE_PKEYS) - int pkey_; -#endif - }; - -#if BUILDFLAG(ENABLE_PKEYS) - // With pkey support, we want to be able to pkey-tag all global metadata - // which requires page granularity. - char one_page_[SystemPageSize()]; -#else - char one_cacheline_[kPartitionCachelineSize]; -#endif - }; - }; -#if BUILDFLAG(ENABLE_PKEYS) - static_assert(sizeof(PoolSetup) % SystemPageSize() == 0, - "PoolSetup has to fill a page(s)"); -#else - static_assert(sizeof(PoolSetup) % kPartitionCachelineSize == 0, - "PoolSetup has to fill a cacheline(s)"); -#endif - - // See the comment describing the address layout above. - // - // These are write-once fields, frequently accessed thereafter. Make sure they - // don't share a cacheline with other, potentially writeable data, through - // alignment and padding. -#if BUILDFLAG(ENABLE_PKEYS) - static_assert(PA_PKEY_ALIGN_SZ >= kPartitionCachelineSize); - alignas(PA_PKEY_ALIGN_SZ) -#else - alignas(kPartitionCachelineSize) -#endif - static PoolSetup setup_ PA_CONSTINIT; - -#if PA_CONFIG(ENABLE_SHADOW_METADATA) - static std::ptrdiff_t regular_pool_shadow_offset_; - static std::ptrdiff_t brp_pool_shadow_offset_; -#endif - -#if BUILDFLAG(ENABLE_PKEYS) - // If we use a pkey pool, we need to tag its metadata with the pkey. Allow the - // function to get access to the PoolSetup. - friend void TagGlobalsWithPkey(int pkey); -#endif -}; - -PA_ALWAYS_INLINE std::pair<pool_handle, uintptr_t> GetPoolAndOffset( - uintptr_t address) { - return PartitionAddressSpace::GetPoolAndOffset(address); -} - -PA_ALWAYS_INLINE pool_handle GetPool(uintptr_t address) { - return std::get<0>(GetPoolAndOffset(address)); -} - -PA_ALWAYS_INLINE uintptr_t OffsetInBRPPool(uintptr_t address) { - return PartitionAddressSpace::OffsetInBRPPool(address); -} - -#if PA_CONFIG(ENABLE_SHADOW_METADATA) -PA_ALWAYS_INLINE std::ptrdiff_t ShadowPoolOffset(pool_handle pool) { - return PartitionAddressSpace::ShadowPoolOffset(pool); -} -#endif - -} // namespace internal - -// Returns false for nullptr. -PA_ALWAYS_INLINE bool IsManagedByPartitionAlloc(uintptr_t address) { - // When ENABLE_BACKUP_REF_PTR_SUPPORT is off, BRP pool isn't used. -#if !BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - PA_DCHECK(!internal::PartitionAddressSpace::IsInBRPPool(address)); -#endif - return internal::PartitionAddressSpace::IsInRegularPool(address) -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - || internal::PartitionAddressSpace::IsInBRPPool(address) -#endif -#if BUILDFLAG(ENABLE_PKEYS) - || internal::PartitionAddressSpace::IsInPkeyPool(address) -#endif - || internal::PartitionAddressSpace::IsInConfigurablePool(address); -} - -// Returns false for nullptr. -PA_ALWAYS_INLINE bool IsManagedByPartitionAllocRegularPool(uintptr_t address) { - return internal::PartitionAddressSpace::IsInRegularPool(address); -} - -// Returns false for nullptr. -PA_ALWAYS_INLINE bool IsManagedByPartitionAllocBRPPool(uintptr_t address) { - return internal::PartitionAddressSpace::IsInBRPPool(address); -} - -#if PA_CONFIG(GLUE_CORE_POOLS) -// Checks whether the address belongs to either regular or BRP pool. -// Returns false for nullptr. -PA_ALWAYS_INLINE bool IsManagedByPartitionAllocCorePools(uintptr_t address) { - return internal::PartitionAddressSpace::IsInCorePools(address); -} -#endif // PA_CONFIG(GLUE_CORE_POOLS) - -// Returns false for nullptr. -PA_ALWAYS_INLINE bool IsManagedByPartitionAllocConfigurablePool( - uintptr_t address) { - return internal::PartitionAddressSpace::IsInConfigurablePool(address); -} - -#if BUILDFLAG(ENABLE_PKEYS) -// Returns false for nullptr. -PA_ALWAYS_INLINE bool IsManagedByPartitionAllocPkeyPool(uintptr_t address) { - return internal::PartitionAddressSpace::IsInPkeyPool(address); -} -#endif - -PA_ALWAYS_INLINE bool IsConfigurablePoolAvailable() { - return internal::PartitionAddressSpace::IsConfigurablePoolInitialized(); -} - -} // namespace partition_alloc - -#endif // BUILDFLAG(HAS_64_BIT_POINTERS) - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ADDRESS_SPACE_H_
diff --git a/base/allocator/partition_allocator/partition_alloc-inl.h b/base/allocator/partition_allocator/partition_alloc-inl.h deleted file mode 100644 index e5725de..0000000 --- a/base/allocator/partition_allocator/partition_alloc-inl.h +++ /dev/null
@@ -1,104 +0,0 @@ -// 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 BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_INL_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_INL_H_ - -#include <algorithm> -#include <cstring> - -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_config.h" -#include "base/allocator/partition_allocator/partition_ref_count.h" -#include "base/allocator/partition_allocator/pkey.h" -#include "base/allocator/partition_allocator/random.h" -#include "base/allocator/partition_allocator/tagging.h" -#include "build/build_config.h" - -// Prefetch *x into memory. -#if defined(__clang__) || defined(COMPILER_GCC) -#define PA_PREFETCH(x) __builtin_prefetch(x) -#else -#define PA_PREFETCH(x) -#endif - -namespace partition_alloc::internal { - -// This is a `memset` that resists being optimized away. Adapted from -// boringssl/src/crypto/mem.c. (Copying and pasting is bad, but //base can't -// depend on //third_party, and this is small enough.) -#if PA_CONFIG(IS_NONCLANG_MSVC) -// MSVC only supports inline assembly on x86. This preprocessor directive -// is intended to be a replacement for the same. -// -// TODO(crbug.com/1351310): Make sure inlining doesn't degrade this into -// a no-op or similar. The documentation doesn't say. -#pragma optimize("", off) -#endif -PA_ALWAYS_INLINE void SecureMemset(void* ptr, uint8_t value, size_t size) { - memset(ptr, value, size); - -#if !PA_CONFIG(IS_NONCLANG_MSVC) - // As best as we can tell, this is sufficient to break any optimisations that - // might try to eliminate "superfluous" memsets. If there's an easy way to - // detect memset_s, it would be better to use that. - __asm__ __volatile__("" : : "r"(ptr) : "memory"); -#endif // !PA_CONFIG(IS_NONCLANG_MSVC) -} -#if PA_CONFIG(IS_NONCLANG_MSVC) -#pragma optimize("", on) -#endif - -#if BUILDFLAG(PA_EXPENSIVE_DCHECKS_ARE_ON) -// Used to memset() memory for debugging purposes only. -PA_ALWAYS_INLINE void DebugMemset(void* ptr, int value, size_t size) { - // Only set the first 512kiB of the allocation. This is enough to detect uses - // of uininitialized / freed memory, and makes tests run significantly - // faster. Note that for direct-mapped allocations, memory is decomitted at - // free() time, so freed memory usage cannot happen. - -#if BUILDFLAG(ENABLE_PKEYS) - LiftPkeyRestrictionsScope lift_pkey_restrictions; -#endif - size_t size_to_memset = std::min(size, size_t{1} << 19); - memset(ptr, value, size_to_memset); -} -#endif // BUILDFLAG(PA_EXPENSIVE_DCHECKS_ARE_ON) - -// Returns true if we've hit the end of a random-length period. We don't want to -// invoke `RandomValue` too often, because we call this function in a hot spot -// (`Free`), and `RandomValue` incurs the cost of atomics. -#if !BUILDFLAG(PA_DCHECK_IS_ON) -PA_ALWAYS_INLINE bool RandomPeriod() { - static thread_local uint8_t counter = 0; - if (PA_UNLIKELY(counter == 0)) { - // It's OK to truncate this value. - counter = static_cast<uint8_t>(RandomValue()); - } - // If `counter` is 0, this will wrap. That is intentional and OK. - counter--; - return counter == 0; -} -#endif // !BUILDFLAG(PA_DCHECK_IS_ON) - -PA_ALWAYS_INLINE uintptr_t ObjectInnerPtr2Addr(const void* ptr) { - return UntagPtr(ptr); -} -PA_ALWAYS_INLINE uintptr_t ObjectPtr2Addr(const void* object) { - // TODO(bartekn): Check that |object| is indeed an object start. - return ObjectInnerPtr2Addr(object); -} -PA_ALWAYS_INLINE void* SlotStartAddr2Ptr(uintptr_t slot_start) { - // TODO(bartekn): Check that |slot_start| is indeed a slot start. - return TagAddr(slot_start); -} -PA_ALWAYS_INLINE uintptr_t SlotStartPtr2Addr(const void* slot_start) { - // TODO(bartekn): Check that |slot_start| is indeed a slot start. - return UntagPtr(slot_start); -} - -} // namespace partition_alloc::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_INL_H_
diff --git a/base/allocator/partition_allocator/partition_alloc.cc b/base/allocator/partition_allocator/partition_alloc.cc deleted file mode 100644 index c4c582a..0000000 --- a/base/allocator/partition_allocator/partition_alloc.cc +++ /dev/null
@@ -1,143 +0,0 @@ -// Copyright 2013 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc.h" - -#include <string.h> - -#include <cstdint> -#include <memory> - -#include "base/allocator/partition_allocator/address_pool_manager.h" -#include "base/allocator/partition_allocator/memory_reclaimer.h" -#include "base/allocator/partition_allocator/partition_address_space.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_hooks.h" -#include "base/allocator/partition_allocator/partition_direct_map_extent.h" -#include "base/allocator/partition_allocator/partition_oom.h" -#include "base/allocator/partition_allocator/partition_page.h" -#include "base/allocator/partition_allocator/partition_root.h" -#include "base/allocator/partition_allocator/partition_stats.h" - -#if BUILDFLAG(USE_STARSCAN) -#include "base/allocator/partition_allocator/starscan/pcscan.h" -#endif - -namespace partition_alloc { - -void PartitionAllocGlobalInit(OomFunction on_out_of_memory) { - // This is from page_allocator_constants.h and doesn't really fit here, but - // there isn't a centralized initialization function in page_allocator.cc, so - // there's no good place in that file to do a STATIC_ASSERT_OR_PA_CHECK. - STATIC_ASSERT_OR_PA_CHECK( - (internal::SystemPageSize() & internal::SystemPageOffsetMask()) == 0, - "SystemPageSize() must be power of 2"); - - // Two partition pages are used as guard / metadata page so make sure the - // super page size is bigger. - STATIC_ASSERT_OR_PA_CHECK( - internal::PartitionPageSize() * 4 <= internal::kSuperPageSize, - "ok super page size"); - STATIC_ASSERT_OR_PA_CHECK( - (internal::kSuperPageSize & internal::SystemPageOffsetMask()) == 0, - "ok super page multiple"); - // Four system pages gives us room to hack out a still-guard-paged piece - // of metadata in the middle of a guard partition page. - STATIC_ASSERT_OR_PA_CHECK( - internal::SystemPageSize() * 4 <= internal::PartitionPageSize(), - "ok partition page size"); - STATIC_ASSERT_OR_PA_CHECK( - (internal::PartitionPageSize() & internal::SystemPageOffsetMask()) == 0, - "ok partition page multiple"); - static_assert(sizeof(internal::PartitionPage<internal::ThreadSafe>) <= - internal::kPageMetadataSize, - "PartitionPage should not be too big"); - STATIC_ASSERT_OR_PA_CHECK( - internal::kPageMetadataSize * internal::NumPartitionPagesPerSuperPage() <= - internal::SystemPageSize(), - "page metadata fits in hole"); - - // Limit to prevent callers accidentally overflowing an int size. - STATIC_ASSERT_OR_PA_CHECK( - internal::MaxDirectMapped() <= - (1UL << 31) + internal::DirectMapAllocationGranularity(), - "maximum direct mapped allocation"); - - // Check that some of our zanier calculations worked out as expected. - static_assert(internal::kSmallestBucket == internal::kAlignment, - "generic smallest bucket"); - static_assert(internal::kMaxBucketed == 983040, "generic max bucketed"); - STATIC_ASSERT_OR_PA_CHECK( - internal::MaxSystemPagesPerRegularSlotSpan() <= 16, - "System pages per slot span must be no greater than 16."); - -#if BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) - STATIC_ASSERT_OR_PA_CHECK( - internal::GetPartitionRefCountIndexMultiplierShift() < - std::numeric_limits<size_t>::max() / 2, - "Calculation in GetPartitionRefCountIndexMultiplierShift() must not " - "underflow."); - // Check that the GetPartitionRefCountIndexMultiplierShift() calculation is - // correct. - STATIC_ASSERT_OR_PA_CHECK( - (1 << internal::GetPartitionRefCountIndexMultiplierShift()) == - (internal::SystemPageSize() / - (sizeof(internal::PartitionRefCount) * - (internal::kSuperPageSize / internal::SystemPageSize()))), - "Bitshift must match the intended multiplication."); - STATIC_ASSERT_OR_PA_CHECK( - ((sizeof(internal::PartitionRefCount) * - (internal::kSuperPageSize / internal::SystemPageSize())) - << internal::GetPartitionRefCountIndexMultiplierShift()) <= - internal::SystemPageSize(), - "PartitionRefCount Bitmap size must be smaller than or equal to " - "<= SystemPageSize()."); -#endif // BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) - - PA_DCHECK(on_out_of_memory); - internal::g_oom_handling_function = on_out_of_memory; -} - -void PartitionAllocGlobalUninitForTesting() { -#if BUILDFLAG(ENABLE_PKEYS) - internal::PartitionAddressSpace::UninitPkeyPoolForTesting(); -#endif - internal::g_oom_handling_function = nullptr; -} - -namespace internal { - -template <bool thread_safe> -PartitionAllocator<thread_safe>::~PartitionAllocator() { - MemoryReclaimer::Instance()->UnregisterPartition(&partition_root_); -} - -template <bool thread_safe> -void PartitionAllocator<thread_safe>::init(PartitionOptions opts) { -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - PA_CHECK(opts.thread_cache == PartitionOptions::ThreadCache::kDisabled) - << "Cannot use a thread cache when PartitionAlloc is malloc()."; -#endif - partition_root_.Init(opts); - MemoryReclaimer::Instance()->RegisterPartition(&partition_root_); -} - -template PartitionAllocator<internal::ThreadSafe>::~PartitionAllocator(); -template void PartitionAllocator<internal::ThreadSafe>::init(PartitionOptions); - -#if (BUILDFLAG(PA_DCHECK_IS_ON) || \ - BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS)) && \ - BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) -void CheckThatSlotOffsetIsZero(uintptr_t address) { - // Add kPartitionPastAllocationAdjustment, because - // PartitionAllocGetSlotStartInBRPPool will subtract it. - PA_CHECK(PartitionAllocGetSlotStartInBRPPool( - address + kPartitionPastAllocationAdjustment) == address); -} -#endif - -} // namespace internal - -} // namespace partition_alloc
diff --git a/base/allocator/partition_allocator/partition_alloc.gni b/base/allocator/partition_allocator/partition_alloc.gni deleted file mode 100644 index e6835a8..0000000 --- a/base/allocator/partition_allocator/partition_alloc.gni +++ /dev/null
@@ -1,311 +0,0 @@ -# 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 = !use_cobalt_customizations && !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" && !use_cobalt_customizations -} -assert(!enable_pkeys || (is_linux && target_cpu == "x64"), - "Pkeys are only supported on x64 linux")
diff --git a/base/allocator/partition_allocator/partition_alloc.h b/base/allocator/partition_allocator/partition_alloc.h deleted file mode 100644 index 23bd85d..0000000 --- a/base/allocator/partition_allocator/partition_alloc.h +++ /dev/null
@@ -1,76 +0,0 @@ -// Copyright 2013 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_H_ - -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_forward.h" -#include "base/allocator/partition_allocator/partition_oom.h" -#include "base/allocator/partition_allocator/partition_root.h" - -// *** HOUSEKEEPING RULES *** -// -// Throughout PartitionAlloc code, we avoid using generic variable names like -// |ptr| or |address|, and prefer names like |object|, |slot_start|, instead. -// This helps emphasize that terms like "object" and "slot" represent two -// different worlds. "Slot" is an indivisible allocation unit, internal to -// PartitionAlloc. It is generally represented as an address (uintptr_t), since -// arithmetic operations on it aren't uncommon, and for that reason it isn't -// MTE-tagged either. "Object" is the allocated memory that the app is given via -// interfaces like Alloc(), Free(), etc. An object is fully contained within a -// slot, and may be surrounded by internal PartitionAlloc structures or empty -// space. Is is generally represented as a pointer to its beginning (most -// commonly void*), and is MTE-tagged so it's safe to access. -// -// The best way to transition between these to worlds is via -// PartitionRoot::ObjectToSlotStart() and ::SlotStartToObject(). These take care -// of shifting between slot/object start, MTE-tagging/untagging and the cast for -// you. There are cases where these functions are insufficient. Internal -// PartitionAlloc structures, like free-list pointers, BRP ref-count, cookie, -// etc. are located in-slot thus accessing them requires an MTE tag. -// SlotStartPtr2Addr() and SlotStartAddr2Ptr() take care of this. -// There are cases where we have to do pointer arithmetic on an object pointer -// (like check belonging to a pool, etc.), in which case we want to strip MTE -// tag. ObjectInnerPtr2Addr() and ObjectPtr2Addr() take care of that. -// -// Avoid using UntagPtr/Addr() and TagPtr/Addr() directly, if possible. And -// definitely avoid using reinterpret_cast between uintptr_t and pointer worlds. -// When you do, add a comment explaining why it's safe from the point of MTE -// tagging. - -namespace partition_alloc { - -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -void PartitionAllocGlobalInit(OomFunction on_out_of_memory); -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -void PartitionAllocGlobalUninitForTesting(); - -namespace internal { -template <bool thread_safe> -struct PA_COMPONENT_EXPORT(PARTITION_ALLOC) PartitionAllocator { - PartitionAllocator() = default; - ~PartitionAllocator(); - - void init(PartitionOptions); - - PA_ALWAYS_INLINE PartitionRoot<thread_safe>* root() { - return &partition_root_; - } - PA_ALWAYS_INLINE const PartitionRoot<thread_safe>* root() const { - return &partition_root_; - } - - private: - PartitionRoot<thread_safe> partition_root_; -}; - -} // namespace internal - -using PartitionAllocator = internal::PartitionAllocator<internal::ThreadSafe>; - -} // namespace partition_alloc - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/README.md b/base/allocator/partition_allocator/partition_alloc_base/README.md deleted file mode 100644 index 06eae02..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/README.md +++ /dev/null
@@ -1,23 +0,0 @@ -# `partition_alloc_base/` - -This is a rough mirror of Chromium's `//base`, cut down to the necessary -files and contents that PartitionAlloc pulls in. Small tweaks (n.b. -macro renaming) have been made to prevent compilation issues, but we -generally prefer that this be a mostly unmutated subset of `//base`. - -## Update Policy - -TBD. - -* This directory may drift out of sync with `//base`. - -* We will merge security updates from Chromium's `//base` once we are - made aware of them. - -* We may elect to freshen files when we need to use new `//base` - functionality in PA. - -## Augmentation Policy - -Prefer not to directly modify contents here. Add them into -`augmentations/`, documenting the usage and provenance of each addition.
diff --git a/base/allocator/partition_allocator/partition_alloc_base/atomic_ref_count.h b/base/allocator/partition_allocator/partition_alloc_base/atomic_ref_count.h deleted file mode 100644 index 9743a64..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/atomic_ref_count.h +++ /dev/null
@@ -1,69 +0,0 @@ -// Copyright 2011 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This is a low level implementation of atomic semantics for reference -// counting. Please use base/memory/ref_counted.h directly instead. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_ATOMIC_REF_COUNT_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_ATOMIC_REF_COUNT_H_ - -#include <atomic> - -namespace partition_alloc::internal::base { - -class AtomicRefCount { - public: - constexpr AtomicRefCount() : ref_count_(0) {} - explicit constexpr AtomicRefCount(int initial_value) - : ref_count_(initial_value) {} - - // Increment a reference count. - // Returns the previous value of the count. - int Increment() { return Increment(1); } - - // Increment a reference count by "increment", which must exceed 0. - // Returns the previous value of the count. - int Increment(int increment) { - return ref_count_.fetch_add(increment, std::memory_order_relaxed); - } - - // Decrement a reference count, and return whether the result is non-zero. - // Insert barriers to ensure that state written before the reference count - // became zero will be visible to a thread that has just made the count zero. - bool Decrement() { - // TODO(jbroman): Technically this doesn't need to be an acquire operation - // unless the result is 1 (i.e., the ref count did indeed reach zero). - // However, there are toolchain issues that make that not work as well at - // present (notably TSAN doesn't like it). - return ref_count_.fetch_sub(1, std::memory_order_acq_rel) != 1; - } - - // Return whether the reference count is one. If the reference count is used - // in the conventional way, a reference count of 1 implies that the current - // thread owns the reference and no other thread shares it. This call - // performs the test for a reference count of one, and performs the memory - // barrier needed for the owning thread to act on the object, knowing that it - // has exclusive access to the object. - bool IsOne() const { return ref_count_.load(std::memory_order_acquire) == 1; } - - // Return whether the reference count is zero. With conventional object - // referencing counting, the object will be destroyed, so the reference count - // should never be zero. Hence this is generally used for a debug check. - bool IsZero() const { - return ref_count_.load(std::memory_order_acquire) == 0; - } - - // Returns the current reference count (with no barriers). This is subtle, and - // should be used only for debugging. - int SubtleRefCountForDebug() const { - return ref_count_.load(std::memory_order_relaxed); - } - - private: - std::atomic_int ref_count_; -}; - -} // namespace partition_alloc::internal::base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_ATOMIC_REF_COUNT_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/augmentations/compiler_specific.h b/base/allocator/partition_allocator/partition_alloc_base/augmentations/compiler_specific.h deleted file mode 100644 index 8ebc003..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/augmentations/compiler_specific.h +++ /dev/null
@@ -1,22 +0,0 @@ -// 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. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_AUGMENTATIONS_COMPILER_SPECIFIC_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_AUGMENTATIONS_COMPILER_SPECIFIC_H_ - -// Extensions for PA's copy of `//base/compiler_specific.h`. - -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" - -// PA_ATTRIBUTE_RETURNS_NONNULL -// -// Tells the compiler that a function never returns a null pointer. -// Sourced from Abseil's `attributes.h`. -#if PA_HAS_ATTRIBUTE(returns_nonnull) -#define PA_ATTRIBUTE_RETURNS_NONNULL __attribute__((returns_nonnull)) -#else -#define PA_ATTRIBUTE_RETURNS_NONNULL -#endif - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_AUGMENTATIONS_COMPILER_SPECIFIC_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/bit_cast.h b/base/allocator/partition_allocator/partition_alloc_base/bit_cast.h deleted file mode 100644 index a6f5cd7..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/bit_cast.h +++ /dev/null
@@ -1,48 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_BIT_CAST_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_BIT_CAST_H_ - -#include <type_traits> - -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" - -#if !PA_HAS_BUILTIN(__builtin_bit_cast) -#include <string.h> // memcpy -#endif - -namespace partition_alloc::internal::base { - -// This is C++20's std::bit_cast<>(). -// It morally does what `*reinterpret_cast<Dest*>(&source)` does, but the -// cast/deref pair is undefined behavior, while bit_cast<>() isn't. -template <class Dest, class Source> -#if PA_HAS_BUILTIN(__builtin_bit_cast) -constexpr -#else -inline -#endif - Dest - bit_cast(const Source& source) { -#if PA_HAS_BUILTIN(__builtin_bit_cast) - // TODO(thakis): Keep only this codepath once nacl is gone or updated. - return __builtin_bit_cast(Dest, source); -#else - static_assert(sizeof(Dest) == sizeof(Source), - "bit_cast requires source and destination to be the same size"); - static_assert(std::is_trivially_copyable_v<Dest>, - "bit_cast requires the destination type to be copyable"); - static_assert(std::is_trivially_copyable_v<Source>, - "bit_cast requires the source type to be copyable"); - - Dest dest; - memcpy(&dest, &source, sizeof(dest)); - return dest; -#endif -} - -} // namespace partition_alloc::internal::base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_BIT_CAST_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/bits.h b/base/allocator/partition_allocator/partition_alloc_base/bits.h deleted file mode 100644 index 5a3fc1d..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/bits.h +++ /dev/null
@@ -1,157 +0,0 @@ -// Copyright 2013 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file defines some bit utilities. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_BITS_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_BITS_H_ - -#include <climits> -#include <cstddef> -#include <cstdint> -#include <type_traits> - -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "build/build_config.h" - -namespace partition_alloc::internal::base::bits { - -// Returns true iff |value| is a power of 2. -template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>> -constexpr bool IsPowerOfTwo(T value) { - // From "Hacker's Delight": Section 2.1 Manipulating Rightmost Bits. - // - // Only positive integers with a single bit set are powers of two. If only one - // bit is set in x (e.g. 0b00000100000000) then |x-1| will have that bit set - // to zero and all bits to its right set to 1 (e.g. 0b00000011111111). Hence - // |x & (x-1)| is 0 iff x is a power of two. - return value > 0 && (value & (value - 1)) == 0; -} - -// Round down |size| to a multiple of alignment, which must be a power of two. -inline constexpr size_t AlignDown(size_t size, size_t alignment) { - PA_DCHECK(IsPowerOfTwo(alignment)); - return size & ~(alignment - 1); -} - -// Move |ptr| back to the previous multiple of alignment, which must be a power -// of two. Defined for types where sizeof(T) is one byte. -template <typename T, typename = typename std::enable_if<sizeof(T) == 1>::type> -inline T* AlignDown(T* ptr, size_t alignment) { - return reinterpret_cast<T*>( - AlignDown(reinterpret_cast<size_t>(ptr), alignment)); -} - -// Round up |size| to a multiple of alignment, which must be a power of two. -inline constexpr size_t AlignUp(size_t size, size_t alignment) { - PA_DCHECK(IsPowerOfTwo(alignment)); - return (size + alignment - 1) & ~(alignment - 1); -} - -// Advance |ptr| to the next multiple of alignment, which must be a power of -// two. Defined for types where sizeof(T) is one byte. -template <typename T, typename = typename std::enable_if<sizeof(T) == 1>::type> -inline T* AlignUp(T* ptr, size_t alignment) { - return reinterpret_cast<T*>( - AlignUp(reinterpret_cast<size_t>(ptr), alignment)); -} - -// CountLeadingZeroBits(value) returns the number of zero bits following the -// most significant 1 bit in |value| if |value| is non-zero, otherwise it -// returns {sizeof(T) * 8}. -// Example: 00100010 -> 2 -// -// CountTrailingZeroBits(value) returns the number of zero bits preceding the -// least significant 1 bit in |value| if |value| is non-zero, otherwise it -// returns {sizeof(T) * 8}. -// Example: 00100010 -> 1 -// -// C does not have an operator to do this, but fortunately the various -// compilers have built-ins that map to fast underlying processor instructions. -// __builtin_clz has undefined behaviour for an input of 0, even though there's -// clearly a return value that makes sense, and even though some processor clz -// instructions have defined behaviour for 0. We could drop to raw __asm__ to -// do better, but we'll avoid doing that unless we see proof that we need to. -template <typename T, int bits = sizeof(T) * 8> -PA_ALWAYS_INLINE constexpr - typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 8, - int>::type - CountLeadingZeroBits(T value) { - static_assert(bits > 0, "invalid instantiation"); -#if defined(COMPILER_MSVC) && !defined(__clang__) - // We would prefer to use the _BitScanReverse(64) intrinsics, but they - // aren't constexpr and thus unusable here. - if (PA_LIKELY(value)) { - int leading_zeros = 0; - constexpr T kMostSignificantBitMask = 1ull << (bits - 1); - for (; !(value & kMostSignificantBitMask); value <<= 1, ++leading_zeros) { - } - return leading_zeros; - } - return bits; -#else - return PA_LIKELY(value) - ? bits == 64 - ? __builtin_clzll(static_cast<uint64_t>(value)) - : __builtin_clz(static_cast<uint32_t>(value)) - (32 - bits) - : bits; -#endif // defined(COMPILER_MSVC) && !defined(__clang__) -} - -template <typename T, int bits = sizeof(T) * 8> -PA_ALWAYS_INLINE constexpr - typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 8, - int>::type - CountTrailingZeroBits(T value) { -#if defined(COMPILER_MSVC) && !defined(__clang__) - // We would prefer to use the _BitScanForward(64) intrinsics, but they - // aren't constexpr and thus unusable here. - if (PA_LIKELY(value)) { - int trailing_zeros = 0; - constexpr T kLeastSignificantBitMask = 1ull; - for (; !(value & kLeastSignificantBitMask); value >>= 1, ++trailing_zeros) { - } - return trailing_zeros; - } - return bits; - -#else - return PA_LIKELY(value) ? bits == 64 - ? __builtin_ctzll(static_cast<uint64_t>(value)) - : __builtin_ctz(static_cast<uint32_t>(value)) - : bits; -#endif // defined(COMPILER_MSVC) && !defined(__clang__) -} - -// Returns the integer i such as 2^i <= n < 2^(i+1). -// -// There is a common `BitLength` function, which returns the number of bits -// required to represent a value. Rather than implement that function, -// use `Log2Floor` and add 1 to the result. -constexpr int Log2Floor(uint32_t n) { - return 31 - CountLeadingZeroBits(n); -} - -// Returns the integer i such as 2^(i-1) < n <= 2^i. -constexpr int Log2Ceiling(uint32_t n) { - // When n == 0, we want the function to return -1. - // When n == 0, (n - 1) will underflow to 0xFFFFFFFF, which is - // why the statement below starts with (n ? 32 : -1). - return (n ? 32 : -1) - CountLeadingZeroBits(n - 1); -} - -// Returns a value of type T with a single bit set in the left-most position. -// Can be used instead of manually shifting a 1 to the left. -template <typename T> -constexpr T LeftmostBit() { - static_assert(std::is_integral<T>::value, - "This function can only be used with integral types."); - T one(1u); - return one << ((CHAR_BIT * sizeof(T) - 1)); -} - -} // namespace partition_alloc::internal::base::bits - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_BITS_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/bits_pa_unittest.cc b/base/allocator/partition_allocator/partition_alloc_base/bits_pa_unittest.cc deleted file mode 100644 index 90d11b8..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/bits_pa_unittest.cc +++ /dev/null
@@ -1,275 +0,0 @@ -// Copyright 2009 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file contains the unit tests for the bit utilities. - -#include "base/allocator/partition_allocator/partition_alloc_base/bits.h" - -#include <cstddef> -#include <limits> - -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace partition_alloc::internal::base::bits { - -TEST(BitsTest, Log2Floor) { - EXPECT_EQ(-1, Log2Floor(0)); - EXPECT_EQ(0, Log2Floor(1)); - EXPECT_EQ(1, Log2Floor(2)); - EXPECT_EQ(1, Log2Floor(3)); - EXPECT_EQ(2, Log2Floor(4)); - for (int i = 3; i < 31; ++i) { - unsigned int value = 1U << i; - EXPECT_EQ(i, Log2Floor(value)); - EXPECT_EQ(i, Log2Floor(value + 1)); - EXPECT_EQ(i, Log2Floor(value + 2)); - EXPECT_EQ(i - 1, Log2Floor(value - 1)); - EXPECT_EQ(i - 1, Log2Floor(value - 2)); - } - EXPECT_EQ(31, Log2Floor(0xffffffffU)); -} - -TEST(BitsTest, Log2Ceiling) { - EXPECT_EQ(-1, Log2Ceiling(0)); - EXPECT_EQ(0, Log2Ceiling(1)); - EXPECT_EQ(1, Log2Ceiling(2)); - EXPECT_EQ(2, Log2Ceiling(3)); - EXPECT_EQ(2, Log2Ceiling(4)); - for (int i = 3; i < 31; ++i) { - unsigned int value = 1U << i; - EXPECT_EQ(i, Log2Ceiling(value)); - EXPECT_EQ(i + 1, Log2Ceiling(value + 1)); - EXPECT_EQ(i + 1, Log2Ceiling(value + 2)); - EXPECT_EQ(i, Log2Ceiling(value - 1)); - EXPECT_EQ(i, Log2Ceiling(value - 2)); - } - EXPECT_EQ(32, Log2Ceiling(0xffffffffU)); -} - -TEST(BitsTest, AlignUp) { - static constexpr size_t kSizeTMax = std::numeric_limits<size_t>::max(); - EXPECT_EQ(0ul, AlignUp(0, 4)); - EXPECT_EQ(4ul, AlignUp(1, 4)); - EXPECT_EQ(4096ul, AlignUp(1, 4096)); - EXPECT_EQ(4096ul, AlignUp(4096, 4096)); - EXPECT_EQ(4096ul, AlignUp(4095, 4096)); - EXPECT_EQ(8192ul, AlignUp(4097, 4096)); - EXPECT_EQ(kSizeTMax - 31, AlignUp(kSizeTMax - 62, 32)); - EXPECT_EQ(kSizeTMax / 2 + 1, AlignUp(1, kSizeTMax / 2 + 1)); -} - -TEST(BitsTest, AlignUpPointer) { - static constexpr uintptr_t kUintPtrTMax = - std::numeric_limits<uintptr_t>::max(); - EXPECT_EQ(reinterpret_cast<uint8_t*>(0), - AlignUp(reinterpret_cast<uint8_t*>(0), 4)); - EXPECT_EQ(reinterpret_cast<uint8_t*>(4), - AlignUp(reinterpret_cast<uint8_t*>(1), 4)); - EXPECT_EQ(reinterpret_cast<uint8_t*>(4096), - AlignUp(reinterpret_cast<uint8_t*>(1), 4096)); - EXPECT_EQ(reinterpret_cast<uint8_t*>(4096), - AlignUp(reinterpret_cast<uint8_t*>(4096), 4096)); - EXPECT_EQ(reinterpret_cast<uint8_t*>(4096), - AlignUp(reinterpret_cast<uint8_t*>(4095), 4096)); - EXPECT_EQ(reinterpret_cast<uint8_t*>(8192), - AlignUp(reinterpret_cast<uint8_t*>(4097), 4096)); - EXPECT_EQ(reinterpret_cast<uint8_t*>(kUintPtrTMax - 31), - AlignUp(reinterpret_cast<uint8_t*>(kUintPtrTMax - 62), 32)); - EXPECT_EQ(reinterpret_cast<uint8_t*>(kUintPtrTMax / 2 + 1), - AlignUp(reinterpret_cast<uint8_t*>(1), kUintPtrTMax / 2 + 1)); -} - -TEST(BitsTest, AlignDown) { - static constexpr size_t kSizeTMax = std::numeric_limits<size_t>::max(); - EXPECT_EQ(0ul, AlignDown(0, 4)); - EXPECT_EQ(0ul, AlignDown(1, 4)); - EXPECT_EQ(0ul, AlignDown(1, 4096)); - EXPECT_EQ(4096ul, AlignDown(4096, 4096)); - EXPECT_EQ(0ul, AlignDown(4095, 4096)); - EXPECT_EQ(4096ul, AlignDown(4097, 4096)); - EXPECT_EQ(kSizeTMax - 63, AlignDown(kSizeTMax - 62, 32)); - EXPECT_EQ(kSizeTMax - 31, AlignDown(kSizeTMax, 32)); - EXPECT_EQ(0ul, AlignDown(1, kSizeTMax / 2 + 1)); -} - -TEST(BitsTest, AlignDownPointer) { - static constexpr uintptr_t kUintPtrTMax = - std::numeric_limits<uintptr_t>::max(); - EXPECT_EQ(reinterpret_cast<uint8_t*>(0), - AlignDown(reinterpret_cast<uint8_t*>(0), 4)); - EXPECT_EQ(reinterpret_cast<uint8_t*>(0), - AlignDown(reinterpret_cast<uint8_t*>(1), 4)); - EXPECT_EQ(reinterpret_cast<uint8_t*>(0), - AlignDown(reinterpret_cast<uint8_t*>(1), 4096)); - EXPECT_EQ(reinterpret_cast<uint8_t*>(4096), - AlignDown(reinterpret_cast<uint8_t*>(4096), 4096)); - EXPECT_EQ(reinterpret_cast<uint8_t*>(0), - AlignDown(reinterpret_cast<uint8_t*>(4095), 4096)); - EXPECT_EQ(reinterpret_cast<uint8_t*>(4096), - AlignDown(reinterpret_cast<uint8_t*>(4097), 4096)); - EXPECT_EQ(reinterpret_cast<uint8_t*>(kUintPtrTMax - 63), - AlignDown(reinterpret_cast<uint8_t*>(kUintPtrTMax - 62), 32)); - EXPECT_EQ(reinterpret_cast<uint8_t*>(kUintPtrTMax - 31), - AlignDown(reinterpret_cast<uint8_t*>(kUintPtrTMax), 32)); - EXPECT_EQ(reinterpret_cast<uint8_t*>(0), - AlignDown(reinterpret_cast<uint8_t*>(1), kUintPtrTMax / 2 + 1)); -} - -TEST(BitsTest, CountLeadingZeroBits8) { - EXPECT_EQ(8, CountLeadingZeroBits(uint8_t{0})); - EXPECT_EQ(7, CountLeadingZeroBits(uint8_t{1})); - for (int shift = 0; shift <= 7; ++shift) { - EXPECT_EQ(7 - shift, - CountLeadingZeroBits(static_cast<uint8_t>(1 << shift))); - } - EXPECT_EQ(4, CountLeadingZeroBits(uint8_t{0x0f})); -} - -TEST(BitsTest, CountLeadingZeroBits16) { - EXPECT_EQ(16, CountLeadingZeroBits(uint16_t{0})); - EXPECT_EQ(15, CountLeadingZeroBits(uint16_t{1})); - for (int shift = 0; shift <= 15; ++shift) { - EXPECT_EQ(15 - shift, - CountLeadingZeroBits(static_cast<uint16_t>(1 << shift))); - } - EXPECT_EQ(4, CountLeadingZeroBits(uint16_t{0x0f0f})); -} - -TEST(BitsTest, CountLeadingZeroBits32) { - EXPECT_EQ(32, CountLeadingZeroBits(uint32_t{0})); - EXPECT_EQ(31, CountLeadingZeroBits(uint32_t{1})); - for (int shift = 0; shift <= 31; ++shift) { - EXPECT_EQ(31 - shift, CountLeadingZeroBits(uint32_t{1} << shift)); - } - EXPECT_EQ(4, CountLeadingZeroBits(uint32_t{0x0f0f0f0f})); -} - -TEST(BitsTest, CountTrailingZeroBits8) { - EXPECT_EQ(8, CountTrailingZeroBits(uint8_t{0})); - EXPECT_EQ(7, CountTrailingZeroBits(uint8_t{128})); - for (int shift = 0; shift <= 7; ++shift) { - EXPECT_EQ(shift, CountTrailingZeroBits(static_cast<uint8_t>(1 << shift))); - } - EXPECT_EQ(4, CountTrailingZeroBits(uint8_t{0xf0})); -} - -TEST(BitsTest, CountTrailingZeroBits16) { - EXPECT_EQ(16, CountTrailingZeroBits(uint16_t{0})); - EXPECT_EQ(15, CountTrailingZeroBits(uint16_t{32768})); - for (int shift = 0; shift <= 15; ++shift) { - EXPECT_EQ(shift, CountTrailingZeroBits(static_cast<uint16_t>(1 << shift))); - } - EXPECT_EQ(4, CountTrailingZeroBits(uint16_t{0xf0f0})); -} - -TEST(BitsTest, CountTrailingZeroBits32) { - EXPECT_EQ(32, CountTrailingZeroBits(uint32_t{0})); - EXPECT_EQ(31, CountTrailingZeroBits(uint32_t{1} << 31)); - for (int shift = 0; shift <= 31; ++shift) { - EXPECT_EQ(shift, CountTrailingZeroBits(uint32_t{1} << shift)); - } - EXPECT_EQ(4, CountTrailingZeroBits(uint32_t{0xf0f0f0f0})); -} - -TEST(BitsTest, CountLeadingZeroBits64) { - EXPECT_EQ(64, CountLeadingZeroBits(uint64_t{0})); - EXPECT_EQ(63, CountLeadingZeroBits(uint64_t{1})); - for (int shift = 0; shift <= 63; ++shift) { - EXPECT_EQ(63 - shift, CountLeadingZeroBits(uint64_t{1} << shift)); - } - EXPECT_EQ(4, CountLeadingZeroBits(uint64_t{0x0f0f0f0f0f0f0f0f})); -} - -TEST(BitsTest, CountTrailingZeroBits64) { - EXPECT_EQ(64, CountTrailingZeroBits(uint64_t{0})); - EXPECT_EQ(63, CountTrailingZeroBits(uint64_t{1} << 63)); - for (int shift = 0; shift <= 31; ++shift) { - EXPECT_EQ(shift, CountTrailingZeroBits(uint64_t{1} << shift)); - } - EXPECT_EQ(4, CountTrailingZeroBits(uint64_t{0xf0f0f0f0f0f0f0f0})); -} - -TEST(BitsTest, CountLeadingZeroBitsSizeT) { -#if defined(ARCH_CPU_64_BITS) - EXPECT_EQ(64, CountLeadingZeroBits(size_t{0})); - EXPECT_EQ(63, CountLeadingZeroBits(size_t{1})); - EXPECT_EQ(32, CountLeadingZeroBits(size_t{1} << 31)); - EXPECT_EQ(1, CountLeadingZeroBits(size_t{1} << 62)); - EXPECT_EQ(0, CountLeadingZeroBits(size_t{1} << 63)); -#else - EXPECT_EQ(32, CountLeadingZeroBits(size_t{0})); - EXPECT_EQ(31, CountLeadingZeroBits(size_t{1})); - EXPECT_EQ(1, CountLeadingZeroBits(size_t{1} << 30)); - EXPECT_EQ(0, CountLeadingZeroBits(size_t{1} << 31)); -#endif // ARCH_CPU_64_BITS -} - -TEST(BitsTest, CountTrailingZeroBitsSizeT) { -#if defined(ARCH_CPU_64_BITS) - EXPECT_EQ(64, CountTrailingZeroBits(size_t{0})); - EXPECT_EQ(63, CountTrailingZeroBits(size_t{1} << 63)); - EXPECT_EQ(31, CountTrailingZeroBits(size_t{1} << 31)); - EXPECT_EQ(1, CountTrailingZeroBits(size_t{2})); - EXPECT_EQ(0, CountTrailingZeroBits(size_t{1})); -#else - EXPECT_EQ(32, CountTrailingZeroBits(size_t{0})); - EXPECT_EQ(31, CountTrailingZeroBits(size_t{1} << 31)); - EXPECT_EQ(1, CountTrailingZeroBits(size_t{2})); - EXPECT_EQ(0, CountTrailingZeroBits(size_t{1})); -#endif // ARCH_CPU_64_BITS -} - -TEST(BitsTest, PowerOfTwo) { - EXPECT_FALSE(IsPowerOfTwo(-1)); - EXPECT_FALSE(IsPowerOfTwo(0)); - EXPECT_TRUE(IsPowerOfTwo(1)); - EXPECT_TRUE(IsPowerOfTwo(2)); - // Unsigned 64 bit cases. - for (uint32_t i = 2; i < 64; i++) { - const uint64_t val = uint64_t{1} << i; - EXPECT_FALSE(IsPowerOfTwo(val - 1)); - EXPECT_TRUE(IsPowerOfTwo(val)); - EXPECT_FALSE(IsPowerOfTwo(val + 1)); - } - // Signed 64 bit cases. - for (uint32_t i = 2; i < 63; i++) { - const int64_t val = int64_t{1} << i; - EXPECT_FALSE(IsPowerOfTwo(val - 1)); - EXPECT_TRUE(IsPowerOfTwo(val)); - EXPECT_FALSE(IsPowerOfTwo(val + 1)); - } - // Signed integers with only the last bit set are negative, not powers of two. - EXPECT_FALSE(IsPowerOfTwo(int64_t{1} << 63)); -} - -TEST(BitsTest, LeftMostBit) { - // Construction of a signed type from an unsigned one of the same width - // preserves all bits. Explicitly confirming this behavior here to illustrate - // correctness of reusing unsigned literals to test behavior of signed types. - // Using signed literals does not work with EXPECT_EQ. - static_assert( - static_cast<int64_t>(0xFFFFFFFFFFFFFFFFu) == 0xFFFFFFFFFFFFFFFFl, - "Comparing signed with unsigned literals compares bits."); - static_assert((0xFFFFFFFFFFFFFFFFu ^ 0xFFFFFFFFFFFFFFFFl) == 0, - "Signed and unsigned literals have the same bits set"); - - uint64_t unsigned_long_long_value = 0x8000000000000000u; - EXPECT_EQ(LeftmostBit<uint64_t>(), unsigned_long_long_value); - EXPECT_EQ(LeftmostBit<int64_t>(), int64_t(unsigned_long_long_value)); - - uint32_t unsigned_long_value = 0x80000000u; - EXPECT_EQ(LeftmostBit<uint32_t>(), unsigned_long_value); - EXPECT_EQ(LeftmostBit<int32_t>(), int32_t(unsigned_long_value)); - - uint16_t unsigned_short_value = 0x8000u; - EXPECT_EQ(LeftmostBit<uint16_t>(), unsigned_short_value); - EXPECT_EQ(LeftmostBit<int16_t>(), int16_t(unsigned_short_value)); - - uint8_t unsigned_byte_value = 0x80u; - EXPECT_EQ(LeftmostBit<uint8_t>(), unsigned_byte_value); - EXPECT_EQ(LeftmostBit<int8_t>(), int8_t(unsigned_byte_value)); -} - -} // namespace partition_alloc::internal::base::bits
diff --git a/base/allocator/partition_allocator/partition_alloc_base/check.cc b/base/allocator/partition_allocator/partition_alloc_base/check.cc deleted file mode 100644 index a33b20e..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/check.cc +++ /dev/null
@@ -1,93 +0,0 @@ -// 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. - -#include "base/allocator/partition_allocator/partition_alloc_base/check.h" - -#include "base/allocator/partition_allocator/partition_alloc_base/logging.h" - -namespace partition_alloc::internal::logging { - -// TODO(1151236): Make CheckError not to allocate memory. So we can use -// CHECK() inside PartitionAllocator when PartitionAllocator-Everywhere is -// enabled. (Also need to modify LogMessage). -CheckError CheckError::Check(const char* file, - int line, - const char* condition) { - CheckError check_error(new LogMessage(file, line, LOGGING_FATAL)); - check_error.stream() << "Check failed: " << condition << ". "; - return check_error; -} - -CheckError CheckError::DCheck(const char* file, - int line, - const char* condition) { - CheckError check_error(new LogMessage(file, line, LOGGING_DCHECK)); - check_error.stream() << "Check failed: " << condition << ". "; - return check_error; -} - -CheckError CheckError::PCheck(const char* file, - int line, - const char* condition) { - SystemErrorCode err_code = logging::GetLastSystemErrorCode(); -#if BUILDFLAG(IS_WIN) - CheckError check_error( - new Win32ErrorLogMessage(file, line, LOGGING_FATAL, err_code)); -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) - CheckError check_error( - new ErrnoLogMessage(file, line, LOGGING_FATAL, err_code)); -#endif - check_error.stream() << "Check failed: " << condition << ". "; - return check_error; -} - -CheckError CheckError::PCheck(const char* file, int line) { - return PCheck(file, line, ""); -} - -CheckError CheckError::DPCheck(const char* file, - int line, - const char* condition) { - SystemErrorCode err_code = logging::GetLastSystemErrorCode(); -#if BUILDFLAG(IS_WIN) - CheckError check_error( - new Win32ErrorLogMessage(file, line, LOGGING_DCHECK, err_code)); -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) - CheckError check_error( - new ErrnoLogMessage(file, line, LOGGING_DCHECK, err_code)); -#endif - check_error.stream() << "Check failed: " << condition << ". "; - return check_error; -} - -CheckError CheckError::NotImplemented(const char* file, - int line, - const char* function) { - CheckError check_error(new LogMessage(file, line, LOGGING_ERROR)); - check_error.stream() << "Not implemented reached in " << function; - return check_error; -} - -std::ostream& CheckError::stream() { - return log_message_->stream(); -} - -CheckError::~CheckError() { - // Note: This function ends up in crash stack traces. If its full name - // changes, the crash server's magic signature logic needs to be updated. - // See cl/306632920. - delete log_message_; -} - -CheckError::CheckError(LogMessage* log_message) : log_message_(log_message) {} - -void RawCheck(const char* message) { - RawLog(LOGGING_FATAL, message); -} - -void RawError(const char* message) { - RawLog(LOGGING_ERROR, message); -} - -} // namespace partition_alloc::internal::logging
diff --git a/base/allocator/partition_allocator/partition_alloc_base/check.h b/base/allocator/partition_allocator/partition_alloc_base/check.h deleted file mode 100644 index 9eb3e7f..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/check.h +++ /dev/null
@@ -1,183 +0,0 @@ -// 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 BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_CHECK_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_CHECK_H_ - -#include <iosfwd> - -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h" - -// This header defines the CHECK, DCHECK, and DPCHECK macros. -// -// CHECK dies with a fatal error if its condition is not true. It is not -// controlled by NDEBUG, so the check will be executed regardless of compilation -// mode. -// -// DCHECK, the "debug mode" check, is enabled depending on NDEBUG and -// DCHECK_ALWAYS_ON, and its severity depends on DCHECK_IS_CONFIGURABLE. -// -// (D)PCHECK is like (D)CHECK, but includes the system error code (c.f. -// perror(3)). -// -// Additional information can be streamed to these macros and will be included -// in the log output if the condition doesn't hold (you may need to include -// <ostream>): -// -// CHECK(condition) << "Additional info."; -// -// The condition is evaluated exactly once. Even in build modes where e.g. -// DCHECK is disabled, the condition and any stream arguments are still -// referenced to avoid warnings about unused variables and functions. -// -// For the (D)CHECK_EQ, etc. macros, see base/check_op.h. However, that header -// is *significantly* larger than check.h, so try to avoid including it in -// header files. - -namespace partition_alloc::internal::logging { - -// Class used to explicitly ignore an ostream, and optionally a boolean value. -class VoidifyStream { - public: - VoidifyStream() = default; - explicit VoidifyStream(bool ignored) {} - - // This operator has lower precedence than << but higher than ?: - void operator&(std::ostream&) {} -}; - -// Helper macro which avoids evaluating the arguments to a stream if the -// condition is false. -#define PA_LAZY_CHECK_STREAM(stream, condition) \ - !(condition) \ - ? (void)0 \ - : ::partition_alloc::internal::logging::VoidifyStream() & (stream) - -// Macro which uses but does not evaluate expr and any stream parameters. -#define PA_EAT_CHECK_STREAM_PARAMS(expr) \ - true ? (void)0 \ - : ::partition_alloc::internal::logging::VoidifyStream(expr) & \ - (*::partition_alloc::internal::logging::g_swallow_stream) -PA_COMPONENT_EXPORT(PARTITION_ALLOC) extern std::ostream* g_swallow_stream; - -class LogMessage; - -// Class used for raising a check error upon destruction. -class PA_COMPONENT_EXPORT(PARTITION_ALLOC) CheckError { - public: - static CheckError Check(const char* file, int line, const char* condition); - - static CheckError DCheck(const char* file, int line, const char* condition); - - static CheckError PCheck(const char* file, int line, const char* condition); - static CheckError PCheck(const char* file, int line); - - static CheckError DPCheck(const char* file, int line, const char* condition); - - static CheckError NotImplemented(const char* file, - int line, - const char* function); - - // Stream for adding optional details to the error message. - std::ostream& stream(); - - PA_NOMERGE ~CheckError(); - - CheckError(const CheckError& other) = delete; - CheckError& operator=(const CheckError& other) = delete; - CheckError(CheckError&& other) = default; - CheckError& operator=(CheckError&& other) = default; - - private: - explicit CheckError(LogMessage* log_message); - - LogMessage* log_message_; -}; - -#if defined(OFFICIAL_BUILD) && !defined(NDEBUG) -#error "Debug builds are not expected to be optimized as official builds." -#endif // defined(OFFICIAL_BUILD) && !defined(NDEBUG) - -#if defined(OFFICIAL_BUILD) && !BUILDFLAG(PA_DCHECK_IS_ON) && !defined(STARBOARD) - -// Discard log strings to reduce code bloat. -// -// This is not calling BreakDebugger since this is called frequently, and -// calling an out-of-line function instead of a noreturn inline macro prevents -// compiler optimizations. -#define PA_BASE_CHECK(condition) \ - PA_UNLIKELY(!(condition)) ? PA_IMMEDIATE_CRASH() \ - : PA_EAT_CHECK_STREAM_PARAMS() - -// TODO(1151236): base/test/gtest_util.h uses CHECK_WILL_STREAM(). After -// copying (or removing) gtest_util.h and removing gtest_uti.h from partition -// allocator's DEPS, rename or remove CHECK_WILL_STREAM(). -#define CHECK_WILL_STREAM() false - -#define PA_BASE_PCHECK(condition) \ - PA_LAZY_CHECK_STREAM( \ - ::partition_alloc::internal::logging::CheckError::PCheck(__FILE__, \ - __LINE__) \ - .stream(), \ - PA_UNLIKELY(!(condition))) - -#else - -#define PA_BASE_CHECK(condition) \ - PA_LAZY_CHECK_STREAM( \ - ::partition_alloc::internal::logging::CheckError::Check( \ - __FILE__, __LINE__, #condition) \ - .stream(), \ - !PA_ANALYZER_ASSUME_TRUE(condition)) - -#define CHECK_WILL_STREAM() true - -#define PA_BASE_PCHECK(condition) \ - PA_LAZY_CHECK_STREAM( \ - ::partition_alloc::internal::logging::CheckError::PCheck( \ - __FILE__, __LINE__, #condition) \ - .stream(), \ - !PA_ANALYZER_ASSUME_TRUE(condition)) - -#endif - -#if BUILDFLAG(PA_DCHECK_IS_ON) - -#define PA_BASE_DCHECK(condition) \ - PA_LAZY_CHECK_STREAM( \ - ::partition_alloc::internal::logging::CheckError::DCheck( \ - __FILE__, __LINE__, #condition) \ - .stream(), \ - !PA_ANALYZER_ASSUME_TRUE(condition)) - -#define PA_BASE_DPCHECK(condition) \ - PA_LAZY_CHECK_STREAM( \ - ::partition_alloc::internal::logging::CheckError::DPCheck( \ - __FILE__, __LINE__, #condition) \ - .stream(), \ - !PA_ANALYZER_ASSUME_TRUE(condition)) - -#else - -#define PA_BASE_DCHECK(condition) PA_EAT_CHECK_STREAM_PARAMS(!(condition)) -#define PA_BASE_DPCHECK(condition) PA_EAT_CHECK_STREAM_PARAMS(!(condition)) - -#endif - -// Async signal safe checking mechanism. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) void RawCheck(const char* message); -PA_COMPONENT_EXPORT(PARTITION_ALLOC) void RawError(const char* message); -#define PA_RAW_CHECK(condition) \ - do { \ - if (!(condition)) \ - ::partition_alloc::internal::logging::RawCheck( \ - "Check failed: " #condition "\n"); \ - } while (0) - -} // namespace partition_alloc::internal::logging - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_CHECK_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h b/base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h deleted file mode 100644 index 77ec1fe..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h +++ /dev/null
@@ -1,233 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_COMPILER_SPECIFIC_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_COMPILER_SPECIFIC_H_ - -#include "build/build_config.h" - -// A wrapper around `__has_attribute`, similar to HAS_CPP_ATTRIBUTE. -#if defined(__has_attribute) -#define PA_HAS_ATTRIBUTE(x) __has_attribute(x) -#else -#define PA_HAS_ATTRIBUTE(x) 0 -#endif - -// A wrapper around `__has_builtin`, similar to HAS_CPP_ATTRIBUTE. -#if defined(__has_builtin) -#define PA_HAS_BUILTIN(x) __has_builtin(x) -#else -#define PA_HAS_BUILTIN(x) 0 -#endif - -// Annotate a function indicating it should not be inlined. -// Use like: -// NOINLINE void DoStuff() { ... } -#if defined(__clang__) && PA_HAS_ATTRIBUTE(noinline) -#define PA_NOINLINE [[clang::noinline]] -#elif defined(COMPILER_GCC) && PA_HAS_ATTRIBUTE(noinline) -#define PA_NOINLINE __attribute__((noinline)) -#elif defined(COMPILER_MSVC) -#define PA_NOINLINE __declspec(noinline) -#else -#define PA_NOINLINE -#endif - -#if defined(__clang__) && defined(NDEBUG) && PA_HAS_ATTRIBUTE(always_inline) -#define PA_ALWAYS_INLINE [[clang::always_inline]] inline -#elif defined(COMPILER_GCC) && defined(NDEBUG) && \ - PA_HAS_ATTRIBUTE(always_inline) -#define PA_ALWAYS_INLINE inline __attribute__((__always_inline__)) -#elif defined(COMPILER_MSVC) && defined(NDEBUG) -#define PA_ALWAYS_INLINE __forceinline -#else -#define PA_ALWAYS_INLINE inline -#endif - -// Annotate a function indicating it should never be tail called. Useful to make -// sure callers of the annotated function are never omitted from call-stacks. -// To provide the complementary behavior (prevent the annotated function from -// being omitted) look at NOINLINE. Also note that this doesn't prevent code -// folding of multiple identical caller functions into a single signature. To -// prevent code folding, see NO_CODE_FOLDING() in base/debug/alias.h. -// Use like: -// void NOT_TAIL_CALLED FooBar(); -#if defined(__clang__) && PA_HAS_ATTRIBUTE(not_tail_called) -#define PA_NOT_TAIL_CALLED [[clang::not_tail_called]] -#else -#define PA_NOT_TAIL_CALLED -#endif - -// Specify memory alignment for structs, classes, etc. -// Use like: -// class PA_ALIGNAS(16) MyClass { ... } -// PA_ALIGNAS(16) int array[4]; -// -// In most places you can use the C++11 keyword "alignas", which is preferred. -// -// Historically, compilers had trouble mixing __attribute__((...)) syntax with -// alignas(...) syntax. However, at least Clang is very accepting nowadays. It -// may be that this macro can be removed entirely. -#if defined(__clang__) -#define PA_ALIGNAS(byte_alignment) alignas(byte_alignment) -#elif defined(COMPILER_MSVC) -#define PA_ALIGNAS(byte_alignment) __declspec(align(byte_alignment)) -#elif defined(COMPILER_GCC) && PA_HAS_ATTRIBUTE(aligned) -#define PA_ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment))) -#endif - -// Tells the compiler a function is using a printf-style format string. -// |format_param| is the one-based index of the format string parameter; -// |dots_param| is the one-based index of the "..." parameter. -// For v*printf functions (which take a va_list), pass 0 for dots_param. -// (This is undocumented but matches what the system C headers do.) -// For member functions, the implicit this parameter counts as index 1. -#if (defined(COMPILER_GCC) || defined(__clang__)) && PA_HAS_ATTRIBUTE(format) -#define PA_PRINTF_FORMAT(format_param, dots_param) \ - __attribute__((format(printf, format_param, dots_param))) -#else -#define PA_PRINTF_FORMAT(format_param, dots_param) -#endif - -// Sanitizers annotations. -#if PA_HAS_ATTRIBUTE(no_sanitize) -#define PA_NO_SANITIZE(what) __attribute__((no_sanitize(what))) -#endif -#if !defined(PA_NO_SANITIZE) -#define PA_NO_SANITIZE(what) -#endif - -// MemorySanitizer annotations. -#if defined(MEMORY_SANITIZER) -#include <sanitizer/msan_interface.h> - -// Mark a memory region fully initialized. -// Use this to annotate code that deliberately reads uninitialized data, for -// example a GC scavenging root set pointers from the stack. -#define PA_MSAN_UNPOISON(p, size) __msan_unpoison(p, size) -#else // MEMORY_SANITIZER -#define PA_MSAN_UNPOISON(p, size) -#endif // MEMORY_SANITIZER - -// Macro for hinting that an expression is likely to be false. -#if !defined(PA_UNLIKELY) -#if defined(COMPILER_GCC) || defined(__clang__) -#define PA_UNLIKELY(x) __builtin_expect(!!(x), 0) -#else -#define PA_UNLIKELY(x) (x) -#endif // defined(COMPILER_GCC) -#endif // !defined(PA_UNLIKELY) - -#if !defined(PA_LIKELY) -#if defined(COMPILER_GCC) || defined(__clang__) -#define PA_LIKELY(x) __builtin_expect(!!(x), 1) -#else -#define PA_LIKELY(x) (x) -#endif // defined(COMPILER_GCC) -#endif // !defined(PA_LIKELY) - -#if !defined(PA_CPU_ARM_NEON) -#if defined(__arm__) -#if !defined(__ARMEB__) && !defined(__ARM_EABI__) && !defined(__EABI__) && \ - !defined(__VFP_FP__) && !defined(_WIN32_WCE) && !defined(ANDROID) -#error Chromium does not support middle endian architecture -#endif -#if defined(__ARM_NEON__) -#define PA_CPU_ARM_NEON 1 -#endif -#endif // defined(__arm__) -#endif // !defined(CPU_ARM_NEON) - -#if !defined(PA_HAVE_MIPS_MSA_INTRINSICS) -#if defined(__mips_msa) && defined(__mips_isa_rev) && (__mips_isa_rev >= 5) -#define PA_HAVE_MIPS_MSA_INTRINSICS 1 -#endif -#endif - -// The ANALYZER_ASSUME_TRUE(bool arg) macro adds compiler-specific hints -// to Clang which control what code paths are statically analyzed, -// and is meant to be used in conjunction with assert & assert-like functions. -// The expression is passed straight through if analysis isn't enabled. -// -// ANALYZER_SKIP_THIS_PATH() suppresses static analysis for the current -// codepath and any other branching codepaths that might follow. -#if defined(__clang_analyzer__) - -namespace partition_alloc::internal { - -inline constexpr bool AnalyzerNoReturn() __attribute__((analyzer_noreturn)) { - return false; -} - -inline constexpr bool AnalyzerAssumeTrue(bool arg) { - // PartitionAllocAnalyzerNoReturn() is invoked and analysis is terminated if - // |arg| is false. - return arg || AnalyzerNoReturn(); -} - -} // namespace partition_alloc::internal - -#define PA_ANALYZER_ASSUME_TRUE(arg) \ - ::partition_alloc::internal::AnalyzerAssumeTrue(!!(arg)) -#define PA_ANALYZER_SKIP_THIS_PATH() \ - static_cast<void>(::partition_alloc::internal::AnalyzerNoReturn()) - -#else // !defined(__clang_analyzer__) - -#define PA_ANALYZER_ASSUME_TRUE(arg) (arg) -#define PA_ANALYZER_SKIP_THIS_PATH() - -#endif // defined(__clang_analyzer__) - -// Use nomerge attribute to disable optimization of merging multiple same calls. -#if defined(__clang__) && PA_HAS_ATTRIBUTE(nomerge) -#define PA_NOMERGE [[clang::nomerge]] -#else -#define PA_NOMERGE -#endif - -// Marks a type as being eligible for the "trivial" ABI despite having a -// non-trivial destructor or copy/move constructor. Such types can be relocated -// after construction by simply copying their memory, which makes them eligible -// to be passed in registers. The canonical example is std::unique_ptr. -// -// Use with caution; this has some subtle effects on constructor/destructor -// ordering and will be very incorrect if the type relies on its address -// remaining constant. When used as a function argument (by value), the value -// may be constructed in the caller's stack frame, passed in a register, and -// then used and destructed in the callee's stack frame. A similar thing can -// occur when values are returned. -// -// TRIVIAL_ABI is not needed for types which have a trivial destructor and -// copy/move constructors, such as base::TimeTicks and other POD. -// -// It is also not likely to be effective on types too large to be passed in one -// or two registers on typical target ABIs. -// -// See also: -// https://clang.llvm.org/docs/AttributeReference.html#trivial-abi -// https://libcxx.llvm.org/docs/DesignDocs/UniquePtrTrivialAbi.html -#if defined(__clang__) && PA_HAS_ATTRIBUTE(trivial_abi) -#define PA_TRIVIAL_ABI [[clang::trivial_abi]] -#else -#define PA_TRIVIAL_ABI -#endif - -// Requires constant initialization. See constinit in C++20. Allows to rely on a -// variable being initialized before execution, and not requiring a global -// constructor. -#if PA_HAS_ATTRIBUTE(require_constant_initialization) -#define PA_CONSTINIT __attribute__((require_constant_initialization)) -#endif -#if !defined(PA_CONSTINIT) -#define PA_CONSTINIT -#endif - -#if defined(__clang__) -#define PA_GSL_POINTER [[gsl::Pointer]] -#else -#define PA_GSL_POINTER -#endif - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_COMPILER_SPECIFIC_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/component_export.h b/base/allocator/partition_allocator/partition_alloc_base/component_export.h deleted file mode 100644 index d396aa2..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/component_export.h +++ /dev/null
@@ -1,80 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_COMPONENT_EXPORT_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_COMPONENT_EXPORT_H_ - -// Used to annotate symbols which are exported by the component named -// |component|. Note that this only does the right thing if the corresponding -// component target's sources are compiled with |IS_$component_IMPL| defined -// as 1. For example: -// -// class PA_COMPONENT_EXPORT(FOO) Bar {}; -// -// If IS_FOO_IMPL=1 at compile time, then Bar will be annotated using the -// PA_COMPONENT_EXPORT_ANNOTATION macro defined below. Otherwise it will be -// annotated using the PA_COMPONENT_IMPORT_ANNOTATION macro. -#define PA_COMPONENT_EXPORT(component) \ - PA_COMPONENT_MACRO_CONDITIONAL_(IS_##component##_IMPL, \ - PA_COMPONENT_EXPORT_ANNOTATION, \ - PA_COMPONENT_IMPORT_ANNOTATION) - -// Indicates whether the current compilation unit is being compiled as part of -// the implementation of the component named |component|. Expands to |1| if -// |IS_$component_IMPL| is defined as |1|; expands to |0| otherwise. -// -// Note in particular that if |IS_$component_IMPL| is not defined at all, it is -// still fine to test PA_INSIDE_COMPONENT_IMPL(component), which expands to |0| -// as expected. -#define PA_INSIDE_COMPONENT_IMPL(component) \ - PA_COMPONENT_MACRO_CONDITIONAL_(IS_##component##_IMPL, 1, 0) - -// Compiler-specific macros to annotate for export or import of a symbol. No-op -// in non-component builds. These should not see much if any direct use. -// Instead use the PA_COMPONENT_EXPORT macro defined above. -#if defined(COMPONENT_BUILD) -#if defined(WIN32) -#define PA_COMPONENT_EXPORT_ANNOTATION __declspec(dllexport) -#define PA_COMPONENT_IMPORT_ANNOTATION __declspec(dllimport) -#else // defined(WIN32) -#define PA_COMPONENT_EXPORT_ANNOTATION __attribute__((visibility("default"))) -#define PA_COMPONENT_IMPORT_ANNOTATION -#endif // defined(WIN32) -#else // defined(COMPONENT_BUILD) -#define PA_COMPONENT_EXPORT_ANNOTATION -#define PA_COMPONENT_IMPORT_ANNOTATION -#endif // defined(COMPONENT_BUILD) - -// Below this point are several internal utility macros used for the -// implementation of the above macros. Not intended for external use. - -// Helper for conditional expansion to one of two token strings. If |condition| -// expands to |1| then this macro expands to |consequent|; otherwise it expands -// to |alternate|. -#define PA_COMPONENT_MACRO_CONDITIONAL_(condition, consequent, alternate) \ - PA_COMPONENT_MACRO_SELECT_THIRD_ARGUMENT_( \ - PA_COMPONENT_MACRO_CONDITIONAL_COMMA_(condition), consequent, alternate) - -// MSVC workaround for __VA_ARGS__ expanding into one expression. -#define PA_MSVC_EXPAND_ARG(arg) arg - -// Expands to a comma (,) iff its first argument expands to |1|. Used in -// conjunction with |PA_COMPONENT_MACRO_SELECT_THIRD_ARGUMENT_()|, as the -// presence or absense of an extra comma can be used to conditionally shift -// subsequent argument positions and thus influence which argument is selected. -#define PA_COMPONENT_MACRO_CONDITIONAL_COMMA_(...) \ - PA_COMPONENT_MACRO_CONDITIONAL_COMMA_IMPL_(__VA_ARGS__, ) -#define PA_COMPONENT_MACRO_CONDITIONAL_COMMA_IMPL_(x, ...) \ - PA_COMPONENT_MACRO_CONDITIONAL_COMMA_##x##_ -#define PA_COMPONENT_MACRO_CONDITIONAL_COMMA_1_ , - -// Helper which simply selects its third argument. Used in conjunction with -// |PA_COMPONENT_MACRO_CONDITIONAL_COMMA_()| above to implement conditional -// macro expansion. -#define PA_COMPONENT_MACRO_SELECT_THIRD_ARGUMENT_(...) \ - PA_MSVC_EXPAND_ARG( \ - PA_COMPONENT_MACRO_SELECT_THIRD_ARGUMENT_IMPL_(__VA_ARGS__)) -#define PA_COMPONENT_MACRO_SELECT_THIRD_ARGUMENT_IMPL_(a, b, c, ...) c - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_COMPONENT_EXPORT_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/component_export_pa_unittest.cc b/base/allocator/partition_allocator/partition_alloc_base/component_export_pa_unittest.cc deleted file mode 100644 index 84f8e43..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/component_export_pa_unittest.cc +++ /dev/null
@@ -1,82 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace partition_alloc::internal::base { -namespace { - -using ComponentExportTest = testing::Test; - -#define IS_TEST_COMPONENT_A_IMPL 1 -#define IS_TEST_COMPONENT_B_IMPL -#define IS_TEST_COMPONENT_C_IMPL 0 -#define IS_TEST_COMPONENT_D_IMPL 2 -#define IS_TEST_COMPONENT_E_IMPL xyz - -TEST(ComponentExportTest, ImportExport) { - // Defined as 1. Treat as export. - EXPECT_EQ(1, PA_INSIDE_COMPONENT_IMPL(TEST_COMPONENT_A)); - - // Defined, but empty. Treat as import. - EXPECT_EQ(0, PA_INSIDE_COMPONENT_IMPL(TEST_COMPONENT_B)); - - // Defined, but 0. Treat as import. - EXPECT_EQ(0, PA_INSIDE_COMPONENT_IMPL(TEST_COMPONENT_C)); - - // Defined, but some other arbitrary thing that isn't 1. Treat as import. - EXPECT_EQ(0, PA_INSIDE_COMPONENT_IMPL(TEST_COMPONENT_D)); - EXPECT_EQ(0, PA_INSIDE_COMPONENT_IMPL(TEST_COMPONENT_E)); - - // Undefined. Treat as import. - EXPECT_EQ(0, PA_INSIDE_COMPONENT_IMPL(TEST_COMPONENT_F)); - - // And just for good measure, ensure that the macros evaluate properly in the - // context of preprocessor #if blocks. -#if PA_INSIDE_COMPONENT_IMPL(TEST_COMPONENT_A) - EXPECT_TRUE(true); -#else - EXPECT_TRUE(false); -#endif - -#if !PA_INSIDE_COMPONENT_IMPL(TEST_COMPONENT_B) - EXPECT_TRUE(true); -#else - EXPECT_TRUE(false); -#endif - -#if !PA_INSIDE_COMPONENT_IMPL(TEST_COMPONENT_C) - EXPECT_TRUE(true); -#else - EXPECT_TRUE(false); -#endif - -#if !PA_INSIDE_COMPONENT_IMPL(TEST_COMPONENT_D) - EXPECT_TRUE(true); -#else - EXPECT_TRUE(false); -#endif - -#if !PA_INSIDE_COMPONENT_IMPL(TEST_COMPONENT_E) - EXPECT_TRUE(true); -#else - EXPECT_TRUE(false); -#endif - -#if !PA_INSIDE_COMPONENT_IMPL(TEST_COMPONENT_F) - EXPECT_TRUE(true); -#else - EXPECT_TRUE(false); -#endif -} - -#undef IS_TEST_COMPONENT_A_IMPL -#undef IS_TEST_COMPONENT_B_IMPL -#undef IS_TEST_COMPONENT_C_IMPL -#undef IS_TEST_COMPONENT_D_IMPL -#undef IS_TEST_COMPONENT_E_IMPL - -} // namespace -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/cpu.cc b/base/allocator/partition_allocator/partition_alloc_base/cpu.cc deleted file mode 100644 index 97bf240..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/cpu.cc +++ /dev/null
@@ -1,203 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/cpu.h" - -#include <inttypes.h> -#include <limits.h> -#include <stddef.h> -#include <stdint.h> -#include <string.h> - -#include <algorithm> -#include <sstream> -#include <utility> - -#include "build/build_config.h" - -#if defined(ARCH_CPU_ARM_FAMILY) && \ - (BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) -#include <asm/hwcap.h> -#include <sys/auxv.h> - -// Temporary definitions until a new hwcap.h is pulled in everywhere. -// https://crbug.com/1265965 -#ifndef HWCAP2_MTE -#define HWCAP2_MTE (1 << 18) -#define HWCAP2_BTI (1 << 17) -#endif -#endif - -#if defined(ARCH_CPU_X86_FAMILY) -#if defined(COMPILER_MSVC) -#include <immintrin.h> // For _xgetbv() -#include <intrin.h> -#endif -#endif - -namespace partition_alloc::internal::base { - -CPU::CPU() { - Initialize(); -} -CPU::CPU(CPU&&) = default; - -namespace { - -#if defined(ARCH_CPU_X86_FAMILY) -#if !defined(COMPILER_MSVC) - -#if defined(__pic__) && defined(__i386__) - -void __cpuid(int cpu_info[4], int info_type) { - __asm__ volatile( - "mov %%ebx, %%edi\n" - "cpuid\n" - "xchg %%edi, %%ebx\n" - : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), - "=d"(cpu_info[3]) - : "a"(info_type), "c"(0)); -} - -#else - -void __cpuid(int cpu_info[4], int info_type) { - __asm__ volatile("cpuid\n" - : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), - "=d"(cpu_info[3]) - : "a"(info_type), "c"(0)); -} - -#endif -#endif // !defined(COMPILER_MSVC) - -// xgetbv returns the value of an Intel Extended Control Register (XCR). -// Currently only XCR0 is defined by Intel so |xcr| should always be zero. -uint64_t xgetbv(uint32_t xcr) { -#if defined(COMPILER_MSVC) - return _xgetbv(xcr); -#else - uint32_t eax, edx; - - __asm__ volatile("xgetbv" : "=a"(eax), "=d"(edx) : "c"(xcr)); - return (static_cast<uint64_t>(edx) << 32) | eax; -#endif // defined(COMPILER_MSVC) -} - -#endif // ARCH_CPU_X86_FAMILY - -} // namespace - -void CPU::Initialize() { -#if defined(ARCH_CPU_X86_FAMILY) - int cpu_info[4] = {-1}; - - // __cpuid with an InfoType argument of 0 returns the number of - // valid Ids in CPUInfo[0] and the CPU identification string in - // the other three array elements. The CPU identification string is - // not in linear order. The code below arranges the information - // in a human readable form. The human readable order is CPUInfo[1] | - // CPUInfo[3] | CPUInfo[2]. CPUInfo[2] and CPUInfo[3] are swapped - // before using memcpy() to copy these three array elements to |cpu_string|. - __cpuid(cpu_info, 0); - int num_ids = cpu_info[0]; - std::swap(cpu_info[2], cpu_info[3]); - - // Interpret CPU feature information. - if (num_ids > 0) { - int cpu_info7[4] = {0}; - __cpuid(cpu_info, 1); - if (num_ids >= 7) { - __cpuid(cpu_info7, 7); - } - signature_ = cpu_info[0]; - stepping_ = cpu_info[0] & 0xf; - type_ = (cpu_info[0] >> 12) & 0x3; - has_mmx_ = (cpu_info[3] & 0x00800000) != 0; - has_sse_ = (cpu_info[3] & 0x02000000) != 0; - has_sse2_ = (cpu_info[3] & 0x04000000) != 0; - has_sse3_ = (cpu_info[2] & 0x00000001) != 0; - has_ssse3_ = (cpu_info[2] & 0x00000200) != 0; - has_sse41_ = (cpu_info[2] & 0x00080000) != 0; - has_sse42_ = (cpu_info[2] & 0x00100000) != 0; - has_popcnt_ = (cpu_info[2] & 0x00800000) != 0; - - // "Hypervisor Present Bit: Bit 31 of ECX of CPUID leaf 0x1." - // See https://lwn.net/Articles/301888/ - // This is checking for any hypervisor. Hypervisors may choose not to - // announce themselves. Hypervisors trap CPUID and sometimes return - // different results to underlying hardware. - is_running_in_vm_ = (cpu_info[2] & 0x80000000) != 0; - - // AVX instructions will generate an illegal instruction exception unless - // a) they are supported by the CPU, - // b) XSAVE is supported by the CPU and - // c) XSAVE is enabled by the kernel. - // See http://software.intel.com/en-us/blogs/2011/04/14/is-avx-enabled - // - // In addition, we have observed some crashes with the xgetbv instruction - // even after following Intel's example code. (See crbug.com/375968.) - // Because of that, we also test the XSAVE bit because its description in - // the CPUID documentation suggests that it signals xgetbv support. - has_avx_ = (cpu_info[2] & 0x10000000) != 0 && - (cpu_info[2] & 0x04000000) != 0 /* XSAVE */ && - (cpu_info[2] & 0x08000000) != 0 /* OSXSAVE */ && - (xgetbv(0) & 6) == 6 /* XSAVE enabled by kernel */; - has_aesni_ = (cpu_info[2] & 0x02000000) != 0; - has_fma3_ = (cpu_info[2] & 0x00001000) != 0; - has_avx2_ = has_avx_ && (cpu_info7[1] & 0x00000020) != 0; - - has_pku_ = (cpu_info7[2] & 0x00000010) != 0; - } - - // Get the brand string of the cpu. - __cpuid(cpu_info, 0x80000000); - const int max_parameter = cpu_info[0]; - - static constexpr int kParameterContainingNonStopTimeStampCounter = 0x80000007; - if (max_parameter >= kParameterContainingNonStopTimeStampCounter) { - __cpuid(cpu_info, kParameterContainingNonStopTimeStampCounter); - has_non_stop_time_stamp_counter_ = (cpu_info[3] & (1 << 8)) != 0; - } - - if (!has_non_stop_time_stamp_counter_ && is_running_in_vm_) { - int cpu_info_hv[4] = {}; - __cpuid(cpu_info_hv, 0x40000000); - if (cpu_info_hv[1] == 0x7263694D && // Micr - cpu_info_hv[2] == 0x666F736F && // osof - cpu_info_hv[3] == 0x76482074) { // t Hv - // If CPUID says we have a variant TSC and a hypervisor has identified - // itself and the hypervisor says it is Microsoft Hyper-V, then treat - // TSC as invariant. - // - // Microsoft Hyper-V hypervisor reports variant TSC as there are some - // scenarios (eg. VM live migration) where the TSC is variant, but for - // our purposes we can treat it as invariant. - has_non_stop_time_stamp_counter_ = true; - } - } -#elif defined(ARCH_CPU_ARM_FAMILY) -#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) - -#if defined(ARCH_CPU_ARM64) - // Check for Armv8.5-A BTI/MTE support, exposed via HWCAP2 - unsigned long hwcap2 = getauxval(AT_HWCAP2); - has_mte_ = hwcap2 & HWCAP2_MTE; - has_bti_ = hwcap2 & HWCAP2_BTI; -#endif - -#elif BUILDFLAG(IS_WIN) - // Windows makes high-resolution thread timing information available in - // user-space. - has_non_stop_time_stamp_counter_ = true; -#endif -#endif -} - -const CPU& CPU::GetInstanceNoAllocation() { - static const CPU cpu; - return cpu; -} - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/cpu.h b/base/allocator/partition_allocator/partition_alloc_base/cpu.h deleted file mode 100644 index 570a2aa..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/cpu.h +++ /dev/null
@@ -1,109 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_CPU_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_CPU_H_ - -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "build/build_config.h" - -namespace partition_alloc::internal::base { - -// Query information about the processor. -class PA_COMPONENT_EXPORT(PARTITION_ALLOC) CPU final { - public: - CPU(); - CPU(CPU&&); - CPU(const CPU&) = delete; - - // Get a preallocated instance of CPU. - // This can be used in very early application startup. The instance of CPU is - // created without branding, see CPU(bool requires_branding) for details and - // implications. - static const CPU& GetInstanceNoAllocation(); - - enum IntelMicroArchitecture { - PENTIUM = 0, - SSE = 1, - SSE2 = 2, - SSE3 = 3, - SSSE3 = 4, - SSE41 = 5, - SSE42 = 6, - AVX = 7, - AVX2 = 8, - FMA3 = 9, - MAX_INTEL_MICRO_ARCHITECTURE = 10 - }; - - // Accessors for CPU information. - int signature() const { return signature_; } - int stepping() const { return stepping_; } - int type() const { return type_; } - bool has_mmx() const { return has_mmx_; } - bool has_sse() const { return has_sse_; } - bool has_sse2() const { return has_sse2_; } - bool has_sse3() const { return has_sse3_; } - bool has_ssse3() const { return has_ssse3_; } - bool has_sse41() const { return has_sse41_; } - bool has_sse42() const { return has_sse42_; } - bool has_popcnt() const { return has_popcnt_; } - bool has_avx() const { return has_avx_; } - bool has_fma3() const { return has_fma3_; } - bool has_avx2() const { return has_avx2_; } - bool has_aesni() const { return has_aesni_; } - bool has_non_stop_time_stamp_counter() const { - return has_non_stop_time_stamp_counter_; - } - bool is_running_in_vm() const { return is_running_in_vm_; } - - // Armv8.5-A extensions for control flow and memory safety. -#if defined(ARCH_CPU_ARM_FAMILY) - bool has_mte() const { return has_mte_; } - bool has_bti() const { return has_bti_; } -#else - constexpr bool has_mte() const { return false; } - constexpr bool has_bti() const { return false; } -#endif - -#if defined(ARCH_CPU_X86_FAMILY) - // Memory protection key support for user-mode pages - bool has_pku() const { return has_pku_; } -#else - constexpr bool has_pku() const { return false; } -#endif - - private: - // Query the processor for CPUID information. - void Initialize(); - - int signature_ = 0; // raw form of type, family, model, and stepping - int type_ = 0; // process type - int stepping_ = 0; // processor revision number - bool has_mmx_ = false; - bool has_sse_ = false; - bool has_sse2_ = false; - bool has_sse3_ = false; - bool has_ssse3_ = false; - bool has_sse41_ = false; - bool has_sse42_ = false; - bool has_popcnt_ = false; - bool has_avx_ = false; - bool has_fma3_ = false; - bool has_avx2_ = false; - bool has_aesni_ = false; -#if defined(ARCH_CPU_ARM_FAMILY) - bool has_mte_ = false; // Armv8.5-A MTE (Memory Taggging Extension) - bool has_bti_ = false; // Armv8.5-A BTI (Branch Target Identification) -#endif -#if defined(ARCH_CPU_X86_FAMILY) - bool has_pku_ = false; -#endif - bool has_non_stop_time_stamp_counter_ = false; - bool is_running_in_vm_ = false; -}; - -} // namespace partition_alloc::internal::base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_CPU_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/cpu_pa_unittest.cc b/base/allocator/partition_allocator/partition_alloc_base/cpu_pa_unittest.cc deleted file mode 100644 index 5c21154..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/cpu_pa_unittest.cc +++ /dev/null
@@ -1,163 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/cpu.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace partition_alloc { - -// Tests whether we can run extended instructions represented by the CPU -// information. This test actually executes some extended instructions (such as -// MMX, SSE, etc.) supported by the CPU and sees we can run them without -// "undefined instruction" exceptions. That is, this test succeeds when this -// test finishes without a crash. -TEST(CPU, RunExtendedInstructions) { - // Retrieve the CPU information. - internal::base::CPU cpu; -#if defined(ARCH_CPU_X86_FAMILY) - - ASSERT_TRUE(cpu.has_mmx()); - ASSERT_TRUE(cpu.has_sse()); - ASSERT_TRUE(cpu.has_sse2()); - ASSERT_TRUE(cpu.has_sse3()); - -// GCC and clang instruction test. -#if defined(COMPILER_GCC) - // Execute an MMX instruction. - __asm__ __volatile__("emms\n" : : : "mm0"); - - // Execute an SSE instruction. - __asm__ __volatile__("xorps %%xmm0, %%xmm0\n" : : : "xmm0"); - - // Execute an SSE 2 instruction. - __asm__ __volatile__("psrldq $0, %%xmm0\n" : : : "xmm0"); - - // Execute an SSE 3 instruction. - __asm__ __volatile__("addsubpd %%xmm0, %%xmm0\n" : : : "xmm0"); - - if (cpu.has_ssse3()) { - // Execute a Supplimental SSE 3 instruction. - __asm__ __volatile__("psignb %%xmm0, %%xmm0\n" : : : "xmm0"); - } - - if (cpu.has_sse41()) { - // Execute an SSE 4.1 instruction. - __asm__ __volatile__("pmuldq %%xmm0, %%xmm0\n" : : : "xmm0"); - } - - if (cpu.has_sse42()) { - // Execute an SSE 4.2 instruction. - __asm__ __volatile__("crc32 %%eax, %%eax\n" : : : "eax"); - } - - if (cpu.has_popcnt()) { - // Execute a POPCNT instruction. - __asm__ __volatile__("popcnt %%eax, %%eax\n" : : : "eax"); - } - - if (cpu.has_avx()) { - // Execute an AVX instruction. - __asm__ __volatile__("vzeroupper\n" : : : "xmm0"); - } - - if (cpu.has_fma3()) { - // Execute a FMA3 instruction. - __asm__ __volatile__("vfmadd132ps %%xmm0, %%xmm0, %%xmm0\n" : : : "xmm0"); - } - - if (cpu.has_avx2()) { - // Execute an AVX 2 instruction. - __asm__ __volatile__("vpunpcklbw %%ymm0, %%ymm0, %%ymm0\n" : : : "xmm0"); - } - - if (cpu.has_pku()) { - // rdpkru - uint32_t pkru; - __asm__ __volatile__(".byte 0x0f,0x01,0xee\n" - : "=a"(pkru) - : "c"(0), "d"(0)); - } -// Visual C 32 bit and ClangCL 32/64 bit test. -#elif defined(COMPILER_MSVC) && \ - (defined(ARCH_CPU_32_BITS) || \ - (defined(ARCH_CPU_64_BITS) && defined(__clang__))) - - // Execute an MMX instruction. - __asm emms; - - // Execute an SSE instruction. - __asm xorps xmm0, xmm0; - - // Execute an SSE 2 instruction. - __asm psrldq xmm0, 0; - - // Execute an SSE 3 instruction. - __asm addsubpd xmm0, xmm0; - - if (cpu.has_ssse3()) { - // Execute a Supplimental SSE 3 instruction. - __asm psignb xmm0, xmm0; - } - - if (cpu.has_sse41()) { - // Execute an SSE 4.1 instruction. - __asm pmuldq xmm0, xmm0; - } - - if (cpu.has_sse42()) { - // Execute an SSE 4.2 instruction. - __asm crc32 eax, eax; - } - - if (cpu.has_popcnt()) { - // Execute a POPCNT instruction. - __asm popcnt eax, eax; - } - - if (cpu.has_avx()) { - // Execute an AVX instruction. - __asm vzeroupper; - } - - if (cpu.has_fma3()) { - // Execute an AVX instruction. - __asm vfmadd132ps xmm0, xmm0, xmm0; - } - - if (cpu.has_avx2()) { - // Execute an AVX 2 instruction. - __asm vpunpcklbw ymm0, ymm0, ymm0 - } -#endif // defined(COMPILER_GCC) -#endif // defined(ARCH_CPU_X86_FAMILY) - -#if defined(ARCH_CPU_ARM64) - // Check that the CPU is correctly reporting support for the Armv8.5-A memory - // tagging extension. The new MTE instructions aren't encoded in NOP space - // like BTI/Pointer Authentication and will crash older cores with a SIGILL if - // used incorrectly. This test demonstrates how it should be done and that - // this approach works. - if (cpu.has_mte()) { -#if !defined(__ARM_FEATURE_MEMORY_TAGGING) - // In this section, we're running on an MTE-compatible core, but we're - // building this file without MTE support. Fail this test to indicate that - // there's a problem with the base/ build configuration. - GTEST_FAIL() - << "MTE support detected (but base/ built without MTE support)"; -#else - char ptr[32]; - uint64_t val; - // Execute a trivial MTE instruction. Normally, MTE should be used via the - // intrinsics documented at - // https://developer.arm.com/documentation/101028/0012/10--Memory-tagging-intrinsics, - // this test uses the irg (Insert Random Tag) instruction directly to make - // sure that it's not optimized out by the compiler. - __asm__ __volatile__("irg %0, %1" : "=r"(val) : "r"(ptr)); -#endif // __ARM_FEATURE_MEMORY_TAGGING - } -#endif // ARCH_CPU_ARM64 -} - -} // namespace partition_alloc
diff --git a/base/allocator/partition_allocator/partition_alloc_base/cxx17_backports.h b/base/allocator/partition_allocator/partition_alloc_base/cxx17_backports.h deleted file mode 100644 index 90306f7..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/cxx17_backports.h +++ /dev/null
@@ -1,35 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_CXX17_BACKPORTS_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_CXX17_BACKPORTS_H_ - -#include <functional> -#include <tuple> -#include <type_traits> -#include <utility> - -#include "base/allocator/partition_allocator/partition_alloc_check.h" - -namespace partition_alloc::internal::base { - -// C++14 implementation of C++17's std::clamp(): -// https://en.cppreference.com/w/cpp/algorithm/clamp -// Please note that the C++ spec makes it undefined behavior to call std::clamp -// with a value of `lo` that compares greater than the value of `hi`. This -// implementation uses a CHECK to enforce this as a hard restriction. -template <typename T, typename Compare> -constexpr const T& clamp(const T& v, const T& lo, const T& hi, Compare comp) { - PA_CHECK(!comp(hi, lo)); - return comp(v, lo) ? lo : comp(hi, v) ? hi : v; -} - -template <typename T> -constexpr const T& clamp(const T& v, const T& lo, const T& hi) { - return base::clamp(v, lo, hi, std::less<T>{}); -} - -} // namespace partition_alloc::internal::base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_CXX17_BACKPORTS_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/cxx17_backports_pa_unittest.cc b/base/allocator/partition_allocator/partition_alloc_base/cxx17_backports_pa_unittest.cc deleted file mode 100644 index 3f60829..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/cxx17_backports_pa_unittest.cc +++ /dev/null
@@ -1,101 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/cxx17_backports.h" - -#include <array> -#include <memory> -#include <tuple> -#include <type_traits> -#include <utility> -#include <vector> - -#include "base/test/gtest_util.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace partition_alloc::internal::base { -namespace { - -struct OneType { - int some_int; -}; - -bool operator<(const OneType& lhs, const OneType& rhs) { - return lhs.some_int < rhs.some_int; -} - -bool operator==(const OneType& lhs, const OneType& rhs) { - return lhs.some_int == rhs.some_int; -} - -struct AnotherType { - int some_other_int; -}; - -bool operator==(const AnotherType& lhs, const AnotherType& rhs) { - return lhs.some_other_int == rhs.some_other_int; -} - -TEST(Cxx17BackportTest, Clamp) { - EXPECT_EQ(0, base::clamp(-5, 0, 10)); - EXPECT_EQ(0, base::clamp(0, 0, 10)); - EXPECT_EQ(3, base::clamp(3, 0, 10)); - EXPECT_EQ(10, base::clamp(10, 0, 10)); - EXPECT_EQ(10, base::clamp(15, 0, 10)); - - EXPECT_EQ(0.0, base::clamp(-5.0, 0.0, 10.0)); - EXPECT_EQ(0.0, base::clamp(0.0, 0.0, 10.0)); - EXPECT_EQ(3.0, base::clamp(3.0, 0.0, 10.0)); - EXPECT_EQ(10.0, base::clamp(10.0, 0.0, 10.0)); - EXPECT_EQ(10.0, base::clamp(15.0, 0.0, 10.0)); - - EXPECT_EQ(0, base::clamp(-5, 0, 0)); - EXPECT_EQ(0, base::clamp(0, 0, 0)); - EXPECT_EQ(0, base::clamp(3, 0, 0)); - - OneType one_type_neg5{-5}; - OneType one_type_0{0}; - OneType one_type_3{3}; - OneType one_type_10{10}; - OneType one_type_15{15}; - - EXPECT_EQ(one_type_0, base::clamp(one_type_neg5, one_type_0, one_type_10)); - EXPECT_EQ(one_type_0, base::clamp(one_type_0, one_type_0, one_type_10)); - EXPECT_EQ(one_type_3, base::clamp(one_type_3, one_type_0, one_type_10)); - EXPECT_EQ(one_type_10, base::clamp(one_type_10, one_type_0, one_type_10)); - EXPECT_EQ(one_type_10, base::clamp(one_type_15, one_type_0, one_type_10)); - - AnotherType another_type_neg5{-5}; - AnotherType another_type_0{0}; - AnotherType another_type_3{3}; - AnotherType another_type_10{10}; - AnotherType another_type_15{15}; - - auto compare_another_type = [](const auto& lhs, const auto& rhs) { - return lhs.some_other_int < rhs.some_other_int; - }; - - EXPECT_EQ(another_type_0, base::clamp(another_type_neg5, another_type_0, - another_type_10, compare_another_type)); - EXPECT_EQ(another_type_0, base::clamp(another_type_0, another_type_0, - another_type_10, compare_another_type)); - EXPECT_EQ(another_type_3, base::clamp(another_type_3, another_type_0, - another_type_10, compare_another_type)); - EXPECT_EQ(another_type_10, - base::clamp(another_type_10, another_type_0, another_type_10, - compare_another_type)); - EXPECT_EQ(another_type_10, - base::clamp(another_type_15, another_type_0, another_type_10, - compare_another_type)); - - EXPECT_CHECK_DEATH(base::clamp(3, 10, 0)); - EXPECT_CHECK_DEATH(base::clamp(3.0, 10.0, 0.0)); - EXPECT_CHECK_DEATH(base::clamp(one_type_3, one_type_10, one_type_0)); - EXPECT_CHECK_DEATH(base::clamp(another_type_3, another_type_10, - another_type_0, compare_another_type)); -} - -} // namespace -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/cxx20_is_constant_evaluated.h b/base/allocator/partition_allocator/partition_alloc_base/cxx20_is_constant_evaluated.h deleted file mode 100644 index a0bb2e4..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/cxx20_is_constant_evaluated.h +++ /dev/null
@@ -1,33 +0,0 @@ -// 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. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_CXX20_IS_CONSTANT_EVALUATED_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_CXX20_IS_CONSTANT_EVALUATED_H_ - -namespace partition_alloc::internal::base { - -// std::is_constant_evaluated was introduced in C++20. PartitionAlloc's minimum -// supported C++ version is C++17. -#if defined(__cpp_lib_is_constant_evaluated) && \ - __cpp_lib_is_constant_evaluated >= 201811L - -#include <type_traits> -using std::is_constant_evaluated; - -#else - -// Implementation of C++20's std::is_constant_evaluated. -// -// References: -// - https://en.cppreference.com/w/cpp/types/is_constant_evaluated -// - https://wg21.link/meta.const.eval -constexpr bool is_constant_evaluated() noexcept { - return __builtin_is_constant_evaluated(); -} - -#endif - -} // namespace partition_alloc::internal::base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_CXX20_IS_CONSTANT_EVALUATED_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/debug/alias.cc b/base/allocator/partition_allocator/partition_alloc_base/debug/alias.cc deleted file mode 100644 index 8f6229c..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/debug/alias.cc +++ /dev/null
@@ -1,15 +0,0 @@ -// Copyright 2011 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/debug/alias.h" - -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" - -namespace partition_alloc::internal::base::debug { - -// This file/function should be excluded from LTO/LTCG to ensure that the -// compiler can't see this function's implementation when compiling calls to it. -PA_NOINLINE void Alias(const void* var) {} - -} // namespace partition_alloc::internal::base::debug
diff --git a/base/allocator/partition_allocator/partition_alloc_base/debug/alias.h b/base/allocator/partition_allocator/partition_alloc_base/debug/alias.h deleted file mode 100644 index ae4e0c6..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/debug/alias.h +++ /dev/null
@@ -1,92 +0,0 @@ -// Copyright 2011 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_DEBUG_ALIAS_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_DEBUG_ALIAS_H_ - -#include <stddef.h> - -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" - -namespace partition_alloc::internal::base::debug { - -// Make the optimizer think that |var| is aliased. This can be used to inhibit -// three different kinds of optimizations: -// -// Case #1: Prevent a local variable from being optimized out if it would not -// otherwise be live at the point of a potential crash. This can only be done -// with local variables, not globals, object members, or function return values -// - these must be copied to locals if you want to ensure they are recorded in -// crash dumps. Function arguments are fine to use since the -// base::debug::Alias() call on them will make sure they are copied to the stack -// even if they were passed in a register. Note that if the local variable is a -// pointer then its value will be retained but the memory that it points to will -// probably not be saved in the crash dump - by default only stack memory is -// saved. Therefore the aliasing technique is usually only worthwhile with -// non-pointer variables. If you have a pointer to an object and you want to -// retain the object's state you need to copy the object or its fields to local -// variables. -// -// Example usage: -// int last_error = err_; -// base::debug::Alias(&last_error); -// char name_copy[16]; -// strncpy(name_copy, p->name, sizeof(name_copy)-1); -// name_copy[sizeof(name_copy)-1] = '\0';; -// base::debug::alias(name_copy); -// CHECK(false); -// -// Case #2: Prevent a tail call into a function. This is useful to make sure the -// function containing the call to base::debug::Alias() will be present in the -// call stack. In this case there is no memory that needs to be on -// the stack so we can use nullptr. The call to base::debug::Alias() needs to -// happen after the call that is suspected to be tail called. Note: This -// technique will prevent tail calls at the specific call site only. To prevent -// them for all invocations of a function look at PA_NOT_TAIL_CALLED. -// -// Example usage: -// PA_NOINLINE void Foo(){ -// ... code ... -// -// Bar(); -// base::debug::Alias(nullptr); -// } -// -// Case #3: Prevent code folding of a non-unique function. Code folding can -// cause the same address to be assigned to different functions if they are -// identical. If finding the precise signature of a function in the call-stack -// is important and it's suspected the function is identical to other functions -// it can be made unique using PA_NO_CODE_FOLDING which is a wrapper around -// base::debug::Alias(); -// -// Example usage: -// PA_NOINLINE void Foo(){ -// PA_NO_CODE_FOLDING(); -// Bar(); -// } -// -// Finally please note that these effects compound. This means that saving a -// stack variable (case #1) using base::debug::Alias() will also inhibit -// tail calls for calls in earlier lines and prevent code folding. - -void PA_COMPONENT_EXPORT(PARTITION_ALLOC) Alias(const void* var); - -} // namespace partition_alloc::internal::base::debug - -// Code folding is a linker optimization whereby the linker identifies functions -// that are bit-identical and overlays them. This saves space but it leads to -// confusing call stacks because multiple symbols are at the same address and -// it is unpredictable which one will be displayed. Disabling of code folding is -// particularly useful when function names are used as signatures in crashes. -// This macro doesn't guarantee that code folding will be prevented but it -// greatly reduces the odds and always prevents it within one source file. -// If using in a function that terminates the process it is safest to put the -// PA_NO_CODE_FOLDING macro at the top of the function. -// Use like: -// void FooBarFailure(size_t size) { PA_NO_CODE_FOLDING(); OOM_CRASH(size); } -#define PA_NO_CODE_FOLDING() \ - const int line_number = __LINE__; \ - ::partition_alloc::internal::base::debug::Alias(&line_number) - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_DEBUG_ALIAS_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/files/file_path.cc b/base/allocator/partition_allocator/partition_alloc_base/files/file_path.cc deleted file mode 100644 index 7425037..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/files/file_path.cc +++ /dev/null
@@ -1,155 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/files/file_path.h" - -#include <string.h> -#include <algorithm> - -#include "base/allocator/partition_allocator/partition_alloc_check.h" - -#if BUILDFLAG(IS_WIN) -#include <windows.h> -#elif BUILDFLAG(IS_APPLE) -#include <CoreFoundation/CoreFoundation.h> -#endif - -namespace partition_alloc::internal::base { - -using StringType = FilePath::StringType; -const FilePath::CharType kStringTerminator = PA_FILE_PATH_LITERAL('\0'); - -// If this FilePath contains a drive letter specification, returns the -// position of the last character of the drive letter specification, -// otherwise returns npos. This can only be true on Windows, when a pathname -// begins with a letter followed by a colon. On other platforms, this always -// returns npos. -StringType::size_type FindDriveLetter(const StringType& path) { -#if defined(PA_FILE_PATH_USES_DRIVE_LETTERS) - // This is dependent on an ASCII-based character set, but that's a - // reasonable assumption. iswalpha can be too inclusive here. - if (path.length() >= 2 && path[1] == L':' && - ((path[0] >= L'A' && path[0] <= L'Z') || - (path[0] >= L'a' && path[0] <= L'z'))) { - return 1; - } -#endif // PA_FILE_PATH_USES_DRIVE_LETTERS - return StringType::npos; -} - -bool IsPathAbsolute(const StringType& path) { -#if defined(PA_FILE_PATH_USES_DRIVE_LETTERS) - StringType::size_type letter = FindDriveLetter(path); - if (letter != StringType::npos) { - // Look for a separator right after the drive specification. - return path.length() > letter + 1 && - FilePath::IsSeparator(path[letter + 1]); - } - // Look for a pair of leading separators. - return path.length() > 1 && FilePath::IsSeparator(path[0]) && - FilePath::IsSeparator(path[1]); -#else // PA_FILE_PATH_USES_DRIVE_LETTERS - // Look for a separator in the first position. - return path.length() > 0 && FilePath::IsSeparator(path[0]); -#endif // PA_FILE_PATH_USES_DRIVE_LETTERS -} - -FilePath::FilePath() = default; - -FilePath::FilePath(const FilePath& that) = default; -FilePath::FilePath(FilePath&& that) noexcept = default; - -FilePath::FilePath(const StringType& path) : path_(path) { - StringType::size_type nul_pos = path_.find(kStringTerminator); - if (nul_pos != StringType::npos) - path_.erase(nul_pos, StringType::npos); -} - -FilePath::~FilePath() = default; - -FilePath& FilePath::operator=(const FilePath& that) = default; - -FilePath& FilePath::operator=(FilePath&& that) noexcept = default; - -// static -bool FilePath::IsSeparator(CharType character) { - for (size_t i = 0; i < kSeparatorsLength - 1; ++i) { - if (character == kSeparators[i]) { - return true; - } - } - - return false; -} - -FilePath FilePath::Append(const StringType& component) const { - StringType appended = component; - StringType without_nuls; - - StringType::size_type nul_pos = component.find(kStringTerminator); - if (nul_pos != StringType::npos) { - without_nuls = component.substr(0, nul_pos); - appended = without_nuls; - } - - PA_DCHECK(!IsPathAbsolute(appended)); - - if (path_.compare(kCurrentDirectory) == 0 && !appended.empty()) { - // Append normally doesn't do any normalization, but as a special case, - // when appending to kCurrentDirectory, just return a new path for the - // component argument. Appending component to kCurrentDirectory would - // serve no purpose other than needlessly lengthening the path, and - // it's likely in practice to wind up with FilePath objects containing - // only kCurrentDirectory when calling DirName on a single relative path - // component. - return FilePath(appended); - } - - FilePath new_path(path_); - new_path.StripTrailingSeparatorsInternal(); - - // Don't append a separator if the path is empty (indicating the current - // directory) or if the path component is empty (indicating nothing to - // append). - if (!appended.empty() && !new_path.path_.empty()) { - // Don't append a separator if the path still ends with a trailing - // separator after stripping (indicating the root directory). - if (!IsSeparator(new_path.path_.back())) { - // Don't append a separator if the path is just a drive letter. - if (FindDriveLetter(new_path.path_) + 1 != new_path.path_.length()) { - new_path.path_.append(1, kSeparators[0]); - } - } - } - - new_path.path_.append(appended.data(), appended.size()); - return new_path; -} - -FilePath FilePath::Append(const FilePath& component) const { - return Append(component.value()); -} - -void FilePath::StripTrailingSeparatorsInternal() { - // If there is no drive letter, start will be 1, which will prevent stripping - // the leading separator if there is only one separator. If there is a drive - // letter, start will be set appropriately to prevent stripping the first - // separator following the drive letter, if a separator immediately follows - // the drive letter. - StringType::size_type start = FindDriveLetter(path_) + 2; - - StringType::size_type last_stripped = StringType::npos; - for (StringType::size_type pos = path_.length(); - pos > start && IsSeparator(path_[pos - 1]); --pos) { - // If the string only has two separators and they're at the beginning, - // don't strip them, unless the string began with more than two separators. - if (pos != start + 1 || last_stripped == start + 2 || - !IsSeparator(path_[start - 1])) { - path_.resize(pos - 1); - last_stripped = pos; - } - } -} - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/files/file_path.h b/base/allocator/partition_allocator/partition_alloc_base/files/file_path.h deleted file mode 100644 index 3fe2e81..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/files/file_path.h +++ /dev/null
@@ -1,231 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// FilePath is a container for pathnames stored in a platform's native string -// type, providing containers for manipulation in according with the -// platform's conventions for pathnames. It supports the following path -// types: -// -// POSIX Windows -// --------------- ---------------------------------- -// Fundamental type char[] wchar_t[] -// Encoding unspecified* UTF-16 -// Separator / \, tolerant of / -// Drive letters no case-insensitive A-Z followed by : -// Alternate root // (surprise!) \\ (2 Separators), for UNC paths -// -// * The encoding need not be specified on POSIX systems, although some -// POSIX-compliant systems do specify an encoding. Mac OS X uses UTF-8. -// Chrome OS also uses UTF-8. -// Linux does not specify an encoding, but in practice, the locale's -// character set may be used. -// -// For more arcane bits of path trivia, see below. -// -// FilePath objects are intended to be used anywhere paths are. An -// application may pass FilePath objects around internally, masking the -// underlying differences between systems, only differing in implementation -// where interfacing directly with the system. For example, a single -// OpenFile(const FilePath &) function may be made available, allowing all -// callers to operate without regard to the underlying implementation. On -// POSIX-like platforms, OpenFile might wrap fopen, and on Windows, it might -// wrap _wfopen_s, perhaps both by calling file_path.value().c_str(). This -// allows each platform to pass pathnames around without requiring conversions -// between encodings, which has an impact on performance, but more imporantly, -// has an impact on correctness on platforms that do not have well-defined -// encodings for pathnames. -// -// Several methods are available to perform common operations on a FilePath -// object, such as determining the parent directory (DirName), isolating the -// final path component (BaseName), and appending a relative pathname string -// to an existing FilePath object (Append). These methods are highly -// recommended over attempting to split and concatenate strings directly. -// These methods are based purely on string manipulation and knowledge of -// platform-specific pathname conventions, and do not consult the filesystem -// at all, making them safe to use without fear of blocking on I/O operations. -// These methods do not function as mutators but instead return distinct -// instances of FilePath objects, and are therefore safe to use on const -// objects. The objects themselves are safe to share between threads. -// -// To aid in initialization of FilePath objects from string literals, a -// FILE_PATH_LITERAL macro is provided, which accounts for the difference -// between char[]-based pathnames on POSIX systems and wchar_t[]-based -// pathnames on Windows. -// -// As a precaution against premature truncation, paths can't contain NULs. -// -// Because a FilePath object should not be instantiated at the global scope, -// instead, use a FilePath::CharType[] and initialize it with -// FILE_PATH_LITERAL. At runtime, a FilePath object can be created from the -// character array. Example: -// -// | const FilePath::CharType kLogFileName[] = FILE_PATH_LITERAL("log.txt"); -// | -// | void Function() { -// | FilePath log_file_path(kLogFileName); -// | [...] -// | } -// -// WARNING: FilePaths should ALWAYS be displayed with LTR directionality, even -// when the UI language is RTL. This means you always need to pass filepaths -// through base::i18n::WrapPathWithLTRFormatting() before displaying it in the -// RTL UI. -// -// This is a very common source of bugs, please try to keep this in mind. -// -// ARCANE BITS OF PATH TRIVIA -// -// - A double leading slash is actually part of the POSIX standard. Systems -// are allowed to treat // as an alternate root, as Windows does for UNC -// (network share) paths. Most POSIX systems don't do anything special -// with two leading slashes, but FilePath handles this case properly -// in case it ever comes across such a system. FilePath needs this support -// for Windows UNC paths, anyway. -// References: -// The Open Group Base Specifications Issue 7, sections 3.267 ("Pathname") -// and 4.12 ("Pathname Resolution"), available at: -// http://www.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_267 -// http://www.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_12 -// -// - Windows treats c:\\ the same way it treats \\. This was intended to -// allow older applications that require drive letters to support UNC paths -// like \\server\share\path, by permitting c:\\server\share\path as an -// equivalent. Since the OS treats these paths specially, FilePath needs -// to do the same. Since Windows can use either / or \ as the separator, -// FilePath treats c://, c:\\, //, and \\ all equivalently. -// Reference: -// The Old New Thing, "Why is a drive letter permitted in front of UNC -// paths (sometimes)?", available at: -// http://blogs.msdn.com/oldnewthing/archive/2005/11/22/495740.aspx - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_FILES_FILE_PATH_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_FILES_FILE_PATH_H_ - -#include <cstddef> -#include <iosfwd> -#include <string> - -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "build/build_config.h" - -// Windows-style drive letter support and pathname separator characters can be -// enabled and disabled independently, to aid testing. These #defines are -// here so that the same setting can be used in both the implementation and -// in the unit test. -#if BUILDFLAG(IS_WIN) -#define PA_FILE_PATH_USES_DRIVE_LETTERS -#define PA_FILE_PATH_USES_WIN_SEPARATORS -#endif // BUILDFLAG(IS_WIN) - -// Macros for string literal initialization of FilePath::CharType[]. -#if BUILDFLAG(IS_WIN) -#define PA_FILE_PATH_LITERAL(x) L##x -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) -#define PA_FILE_PATH_LITERAL(x) x -#endif // BUILDFLAG(IS_WIN) - -namespace partition_alloc::internal::base { - -// An abstraction to isolate users from the differences between native -// pathnames on different platforms. -class PA_COMPONENT_EXPORT(PARTITION_ALLOC) FilePath { - public: -#if BUILDFLAG(IS_WIN) - // On Windows, for Unicode-aware applications, native pathnames are wchar_t - // arrays encoded in UTF-16. - typedef std::wstring StringType; -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) - // On most platforms, native pathnames are char arrays, and the encoding - // may or may not be specified. On Mac OS X, native pathnames are encoded - // in UTF-8. - typedef std::string StringType; -#endif // BUILDFLAG(IS_WIN) - - typedef StringType::value_type CharType; - - // Null-terminated array of separators used to separate components in paths. - // Each character in this array is a valid separator, but kSeparators[0] is - // treated as the canonical separator and is used when composing pathnames. - static constexpr CharType kSeparators[] = -#if defined(PA_FILE_PATH_USES_WIN_SEPARATORS) - PA_FILE_PATH_LITERAL("\\/"); -#else // PA_FILE_PATH_USES_WIN_SEPARATORS - PA_FILE_PATH_LITERAL("/"); -#endif // PA_FILE_PATH_USES_WIN_SEPARATORS - - // std::size(kSeparators), i.e., the number of separators in kSeparators plus - // one (the null terminator at the end of kSeparators). - static constexpr size_t kSeparatorsLength = std::size(kSeparators); - - // The special path component meaning "this directory." - static constexpr CharType kCurrentDirectory[] = PA_FILE_PATH_LITERAL("."); - - // The special path component meaning "the parent directory." - static constexpr CharType kParentDirectory[] = PA_FILE_PATH_LITERAL(".."); - - // The character used to identify a file extension. - static constexpr CharType kExtensionSeparator = PA_FILE_PATH_LITERAL('.'); - - FilePath(); - FilePath(const FilePath& that); - explicit FilePath(const StringType& that); - ~FilePath(); - FilePath& operator=(const FilePath& that); - - // Constructs FilePath with the contents of |that|, which is left in valid but - // unspecified state. - FilePath(FilePath&& that) noexcept; - // Replaces the contents with those of |that|, which is left in valid but - // unspecified state. - FilePath& operator=(FilePath&& that) noexcept; - - // Required for some STL containers and operations - bool operator<(const FilePath& that) const { return path_ < that.path_; } - - const StringType& value() const { return path_; } - - [[nodiscard]] bool empty() const { return path_.empty(); } - - void clear() { path_.clear(); } - - // Returns true if |character| is in kSeparators. - static bool IsSeparator(CharType character); - - // Returns a FilePath by appending a separator and the supplied path - // component to this object's path. Append takes care to avoid adding - // excessive separators if this object's path already ends with a separator. - // If this object's path is kCurrentDirectory, a new FilePath corresponding - // only to |component| is returned. |component| must be a relative path; - // it is an error to pass an absolute path. - [[nodiscard]] FilePath Append(const FilePath& component) const; - [[nodiscard]] FilePath Append(const StringType& component) const; - - private: - // Remove trailing separators from this object. If the path is absolute, it - // will never be stripped any more than to refer to the absolute root - // directory, so "////" will become "/", not "". A leading pair of - // separators is never stripped, to support alternate roots. This is used to - // support UNC paths on Windows. - void StripTrailingSeparatorsInternal(); - - StringType path_; -}; - -} // namespace partition_alloc::internal::base - -namespace std { - -template <> -struct hash<::partition_alloc::internal::base::FilePath> { - typedef ::partition_alloc::internal::base::FilePath argument_type; - typedef std::size_t result_type; - result_type operator()(argument_type const& f) const { - return hash<::partition_alloc::internal::base::FilePath::StringType>()( - f.value()); - } -}; - -} // namespace std - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_FILES_FILE_PATH_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/files/file_path_pa_unittest.cc b/base/allocator/partition_allocator/partition_alloc_base/files/file_path_pa_unittest.cc deleted file mode 100644 index 5542132..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/files/file_path_pa_unittest.cc +++ /dev/null
@@ -1,154 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/files/file_path.h" - -#include <stddef.h> - -#include <sstream> - -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -// This macro helps avoid wrapped lines in the test structs. -#define FPL(x) PA_FILE_PATH_LITERAL(x) - -// This macro constructs strings which can contain NULs. -#define FPS(x) FilePath::StringType(FPL(x), std::size(FPL(x)) - 1) - -namespace partition_alloc::internal::base { - -struct UnaryTestData { - FilePath::StringType input; - FilePath::StringType expected; -}; - -struct UnaryBooleanTestData { - FilePath::StringType input; - bool expected; -}; - -struct BinaryTestData { - FilePath::StringType inputs[2]; - FilePath::StringType expected; -}; - -struct BinaryBooleanTestData { - FilePath::StringType inputs[2]; - bool expected; -}; - -struct BinaryIntTestData { - FilePath::StringType inputs[2]; - int expected; -}; - -TEST(PartitionAllocBaseFilePathTest, Append) { - const struct BinaryTestData cases[] = { - {{FPL(""), FPL("cc")}, FPL("cc")}, - {{FPL("."), FPL("ff")}, FPL("ff")}, - {{FPL("."), FPL("")}, FPL(".")}, - {{FPL("/"), FPL("cc")}, FPL("/cc")}, - {{FPL("/aa"), FPL("")}, FPL("/aa")}, - {{FPL("/aa/"), FPL("")}, FPL("/aa")}, - {{FPL("//aa"), FPL("")}, FPL("//aa")}, - {{FPL("//aa/"), FPL("")}, FPL("//aa")}, - {{FPL("//"), FPL("aa")}, FPL("//aa")}, -#if defined(PA_FILE_PATH_USES_DRIVE_LETTERS) - {{FPL("c:"), FPL("a")}, FPL("c:a")}, - {{FPL("c:"), FPL("")}, FPL("c:")}, - {{FPL("c:/"), FPL("a")}, FPL("c:/a")}, - {{FPL("c://"), FPL("a")}, FPL("c://a")}, - {{FPL("c:///"), FPL("a")}, FPL("c:/a")}, -#endif // PA_FILE_PATH_USES_DRIVE_LETTERS -#if defined(PA_FILE_PATH_USES_WIN_SEPARATORS) - // Append introduces the default separator character, so these test cases - // need to be defined with different expected results on platforms that use - // different default separator characters. - {{FPL("\\"), FPL("cc")}, FPL("\\cc")}, - {{FPL("\\aa"), FPL("")}, FPL("\\aa")}, - {{FPL("\\aa\\"), FPL("")}, FPL("\\aa")}, - {{FPL("\\\\aa"), FPL("")}, FPL("\\\\aa")}, - {{FPL("\\\\aa\\"), FPL("")}, FPL("\\\\aa")}, - {{FPL("\\\\"), FPL("aa")}, FPL("\\\\aa")}, - {{FPL("/aa/bb"), FPL("cc")}, FPL("/aa/bb\\cc")}, - {{FPL("/aa/bb/"), FPL("cc")}, FPL("/aa/bb\\cc")}, - {{FPL("aa/bb/"), FPL("cc")}, FPL("aa/bb\\cc")}, - {{FPL("aa/bb"), FPL("cc")}, FPL("aa/bb\\cc")}, - {{FPL("a/b"), FPL("c")}, FPL("a/b\\c")}, - {{FPL("a/b/"), FPL("c")}, FPL("a/b\\c")}, - {{FPL("//aa"), FPL("bb")}, FPL("//aa\\bb")}, - {{FPL("//aa/"), FPL("bb")}, FPL("//aa\\bb")}, - {{FPL("\\aa\\bb"), FPL("cc")}, FPL("\\aa\\bb\\cc")}, - {{FPL("\\aa\\bb\\"), FPL("cc")}, FPL("\\aa\\bb\\cc")}, - {{FPL("aa\\bb\\"), FPL("cc")}, FPL("aa\\bb\\cc")}, - {{FPL("aa\\bb"), FPL("cc")}, FPL("aa\\bb\\cc")}, - {{FPL("a\\b"), FPL("c")}, FPL("a\\b\\c")}, - {{FPL("a\\b\\"), FPL("c")}, FPL("a\\b\\c")}, - {{FPL("\\\\aa"), FPL("bb")}, FPL("\\\\aa\\bb")}, - {{FPL("\\\\aa\\"), FPL("bb")}, FPL("\\\\aa\\bb")}, -#if defined(PA_FILE_PATH_USES_DRIVE_LETTERS) - {{FPL("c:\\"), FPL("a")}, FPL("c:\\a")}, - {{FPL("c:\\\\"), FPL("a")}, FPL("c:\\\\a")}, - {{FPL("c:\\\\\\"), FPL("a")}, FPL("c:\\a")}, - {{FPL("c:\\"), FPL("")}, FPL("c:\\")}, - {{FPL("c:\\a"), FPL("b")}, FPL("c:\\a\\b")}, - {{FPL("c:\\a\\"), FPL("b")}, FPL("c:\\a\\b")}, -#endif // PA_FILE_PATH_USES_DRIVE_LETTERS -#else // PA_FILE_PATH_USES_WIN_SEPARATORS - {{FPL("/aa/bb"), FPL("cc")}, FPL("/aa/bb/cc")}, - {{FPL("/aa/bb/"), FPL("cc")}, FPL("/aa/bb/cc")}, - {{FPL("aa/bb/"), FPL("cc")}, FPL("aa/bb/cc")}, - {{FPL("aa/bb"), FPL("cc")}, FPL("aa/bb/cc")}, - {{FPL("a/b"), FPL("c")}, FPL("a/b/c")}, - {{FPL("a/b/"), FPL("c")}, FPL("a/b/c")}, - {{FPL("//aa"), FPL("bb")}, FPL("//aa/bb")}, - {{FPL("//aa/"), FPL("bb")}, FPL("//aa/bb")}, -#if defined(PA_FILE_PATH_USES_DRIVE_LETTERS) - {{FPL("c:/"), FPL("a")}, FPL("c:/a")}, - {{FPL("c:/"), FPL("")}, FPL("c:/")}, - {{FPL("c:/a"), FPL("b")}, FPL("c:/a/b")}, - {{FPL("c:/a/"), FPL("b")}, FPL("c:/a/b")}, -#endif // PA_FILE_PATH_USES_DRIVE_LETTERS -#endif // PA_FILE_PATH_USES_WIN_SEPARATORS - }; - - for (size_t i = 0; i < std::size(cases); ++i) { - FilePath root(cases[i].inputs[0]); - FilePath::StringType leaf(cases[i].inputs[1]); - FilePath observed_str = root.Append(leaf); - EXPECT_EQ(FilePath::StringType(cases[i].expected), observed_str.value()) - << "i: " << i << ", root: " << root.value() << ", leaf: " << leaf; - FilePath observed_path = root.Append(FilePath(leaf)); - EXPECT_EQ(FilePath::StringType(cases[i].expected), observed_path.value()) - << "i: " << i << ", root: " << root.value() << ", leaf: " << leaf; - } -} - -TEST(PartitionAllocBaseFilePathTest, ConstructWithNUL) { - // Assert FPS() works. - ASSERT_EQ(3U, FPS("a\0b").length()); - - // Test constructor strips '\0' - FilePath path(FPS("a\0b")); - EXPECT_EQ(1U, path.value().length()); - EXPECT_EQ(FPL("a"), path.value()); -} - -TEST(PartitionAllocBaseFilePathTest, AppendWithNUL) { - // Assert FPS() works. - ASSERT_EQ(3U, FPS("b\0b").length()); - - // Test Append() strips '\0' - FilePath path(FPL("a")); - path = path.Append(FPS("b\0b")); - EXPECT_EQ(3U, path.value().length()); -#if defined(PA_FILE_PATH_USES_WIN_SEPARATORS) - EXPECT_EQ(FPL("a\\b"), path.value()); -#else - EXPECT_EQ(FPL("a/b"), path.value()); -#endif -} - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/files/file_util.h b/base/allocator/partition_allocator/partition_alloc_base/files/file_util.h deleted file mode 100644 index 4bc5bd6..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/files/file_util.h +++ /dev/null
@@ -1,37 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file contains utility functions for dealing with the local -// filesystem. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_FILES_FILE_UTIL_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_FILES_FILE_UTIL_H_ - -#include <stddef.h> -#include <stdint.h> -#include <stdio.h> - -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "build/build_config.h" - -#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) -#include <sys/stat.h> -#include <unistd.h> -#endif - -namespace partition_alloc::internal::base { - -#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) - -// Read exactly |bytes| bytes from file descriptor |fd|, storing the result -// in |buffer|. This function is protected against EINTR and partial reads. -// Returns true iff |bytes| bytes have been successfully read from |fd|. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -bool ReadFromFD(int fd, char* buffer, size_t bytes); - -#endif // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) - -} // namespace partition_alloc::internal::base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_FILES_FILE_UTIL_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/files/file_util_posix.cc b/base/allocator/partition_allocator/partition_alloc_base/files/file_util_posix.cc deleted file mode 100644 index 4853ff3..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/files/file_util_posix.cc +++ /dev/null
@@ -1,23 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/files/file_util.h" - -#include "base/allocator/partition_allocator/partition_alloc_base/posix/eintr_wrapper.h" - -namespace partition_alloc::internal::base { - -bool ReadFromFD(int fd, char* buffer, size_t bytes) { - size_t total_read = 0; - while (total_read < bytes) { - ssize_t bytes_read = - PA_HANDLE_EINTR(read(fd, buffer + total_read, bytes - total_read)); - if (bytes_read <= 0) - break; - total_read += bytes_read; - } - return total_read == bytes; -} - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/fuchsia/fuchsia_logging.cc b/base/allocator/partition_allocator/partition_alloc_base/fuchsia/fuchsia_logging.cc deleted file mode 100644 index 84663a9..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/fuchsia/fuchsia_logging.cc +++ /dev/null
@@ -1,26 +0,0 @@ -// Copyright 2017 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/fuchsia/fuchsia_logging.h" - -#include <zircon/status.h> - -#include <iomanip> - -namespace partition_alloc::internal::logging { - -ZxLogMessage::ZxLogMessage(const char* file_path, - int line, - LogSeverity severity, - zx_status_t zx_err) - : LogMessage(file_path, line, severity), zx_err_(zx_err) {} - -ZxLogMessage::~ZxLogMessage() { - // zx_status_t error values are negative, so log the numeric version as - // decimal rather than hex. This is also useful to match zircon/errors.h for - // grepping. - stream() << ": " << zx_status_get_string(zx_err_) << " (" << zx_err_ << ")"; -} - -} // namespace partition_alloc::internal::logging
diff --git a/base/allocator/partition_allocator/partition_alloc_base/fuchsia/fuchsia_logging.h b/base/allocator/partition_allocator/partition_alloc_base/fuchsia/fuchsia_logging.h deleted file mode 100644 index c0cf1b7..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/fuchsia/fuchsia_logging.h +++ /dev/null
@@ -1,70 +0,0 @@ -// Copyright 2017 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_FUCHSIA_FUCHSIA_LOGGING_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_FUCHSIA_FUCHSIA_LOGGING_H_ - -#include <lib/fit/function.h> -#include <zircon/types.h> - -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_base/logging.h" -#include "build/build_config.h" - -// Use the PA_ZX_LOG family of macros along with a zx_status_t containing a -// Zircon error. The error value will be decoded so that logged messages explain -// the error. - -namespace partition_alloc::internal::logging { - -class PA_COMPONENT_EXPORT(PARTITION_ALLOC) ZxLogMessage - : public logging::LogMessage { - public: - ZxLogMessage(const char* file_path, - int line, - LogSeverity severity, - zx_status_t zx_err); - - ZxLogMessage(const ZxLogMessage&) = delete; - ZxLogMessage& operator=(const ZxLogMessage&) = delete; - - ~ZxLogMessage() override; - - private: - zx_status_t zx_err_; -}; - -} // namespace partition_alloc::internal::logging - -#define PA_ZX_LOG_STREAM(severity, zx_err) \ - PA_COMPACT_GOOGLE_LOG_EX_##severity(ZxLogMessage, zx_err).stream() - -#define PA_ZX_LOG(severity, zx_err) \ - PA_LAZY_STREAM(PA_ZX_LOG_STREAM(severity, zx_err), PA_LOG_IS_ON(severity)) -#define PA_ZX_LOG_IF(severity, condition, zx_err) \ - PA_LAZY_STREAM(PA_ZX_LOG_STREAM(severity, zx_err), \ - PA_LOG_IS_ON(severity) && (condition)) - -#define PA_ZX_CHECK(condition, zx_err) \ - PA_LAZY_STREAM(PA_ZX_LOG_STREAM(FATAL, zx_err), !(condition)) \ - << "Check failed: " #condition << ". " - -#define PA_ZX_DLOG(severity, zx_err) \ - PA_LAZY_STREAM(PA_ZX_LOG_STREAM(severity, zx_err), PA_DLOG_IS_ON(severity)) - -#if BUILDFLAG(PA_DCHECK_IS_ON) -#define PA_ZX_DLOG_IF(severity, condition, zx_err) \ - PA_LAZY_STREAM(PA_ZX_LOG_STREAM(severity, zx_err), \ - PA_DLOG_IS_ON(severity) && (condition)) -#else // BUILDFLAG(PA_DCHECK_IS_ON) -#define PA_ZX_DLOG_IF(severity, condition, zx_err) PA_EAT_STREAM_PARAMETERS -#endif // BUILDFLAG(PA_DCHECK_IS_ON) - -#define PA_ZX_DCHECK(condition, zx_err) \ - PA_LAZY_STREAM(PA_ZX_LOG_STREAM(DCHECK, zx_err), \ - BUILDFLAG(PA_DCHECK_IS_ON) && !(condition)) \ - << "Check failed: " #condition << ". " - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_FUCHSIA_FUCHSIA_LOGGING_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/fuchsia/fuchsia_logging_pa_unittest.cc b/base/allocator/partition_allocator/partition_alloc_base/fuchsia/fuchsia_logging_pa_unittest.cc deleted file mode 100644 index 6790cff..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/fuchsia/fuchsia_logging_pa_unittest.cc +++ /dev/null
@@ -1,52 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/fuchsia/fuchsia_logging.h" - -#include <fuchsia/logger/cpp/fidl.h> -#include <lib/fidl/cpp/binding.h> -#include <lib/sys/cpp/component_context.h> - -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_base/logging.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace partition_alloc::internal::base { - -namespace { - -class MockLogSource { - public: - MOCK_METHOD0(Log, const char*()); -}; - -} // namespace - -// Verifies the Fuchsia-specific PA_ZX_*() logging macros. -TEST(FuchsiaLoggingTest, FuchsiaLogging) { - MockLogSource mock_log_source; - constexpr int kTimes = -#if BUILDFLAG(PA_DCHECK_IS_ON) - 2; -#else - 1; -#endif - EXPECT_CALL(mock_log_source, Log()) - .Times(kTimes) - .WillRepeatedly(testing::Return("log message")); - - logging::SetMinLogLevel(logging::LOGGING_INFO); - - EXPECT_TRUE(PA_LOG_IS_ON(INFO)); - EXPECT_EQ(BUILDFLAG(PA_DCHECK_IS_ON), PA_DLOG_IS_ON(INFO)); - - PA_ZX_LOG(INFO, ZX_ERR_INTERNAL) << mock_log_source.Log(); - PA_ZX_DLOG(INFO, ZX_ERR_INTERNAL) << mock_log_source.Log(); - - PA_ZX_CHECK(true, ZX_ERR_INTERNAL); - PA_ZX_DCHECK(true, ZX_ERR_INTERNAL); -} - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/gtest_prod_util.h b/base/allocator/partition_allocator/partition_alloc_base/gtest_prod_util.h deleted file mode 100644 index c0065f6..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/gtest_prod_util.h +++ /dev/null
@@ -1,66 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_GTEST_PROD_UTIL_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_GTEST_PROD_UTIL_H_ - -#include "testing/gtest/include/gtest/gtest_prod.h" // nogncheck - -// This is a wrapper for gtest's FRIEND_TEST macro that friends -// test with all possible prefixes. This is very helpful when changing the test -// prefix, because the friend declarations don't need to be updated. -// -// Example usage: -// -// class MyClass { -// private: -// void MyMethod(); -// PA_FRIEND_TEST_ALL_PREFIXES(MyClassTest, MyMethod); -// }; -#define PA_FRIEND_TEST_ALL_PREFIXES(test_case_name, test_name) \ - FRIEND_TEST(test_case_name, test_name); \ - FRIEND_TEST(test_case_name, DISABLED_##test_name); \ - FRIEND_TEST(test_case_name, FLAKY_##test_name) - -// C++ compilers will refuse to compile the following code: -// -// namespace foo { -// class MyClass { -// private: -// PA_FRIEND_TEST_ALL_PREFIXES(MyClassTest, TestMethod); -// bool private_var; -// }; -// } // namespace foo -// -// class MyClassTest::TestMethod() { -// foo::MyClass foo_class; -// foo_class.private_var = true; -// } -// -// Unless you forward declare MyClassTest::TestMethod outside of namespace foo. -// Use PA_FORWARD_DECLARE_TEST to do so for all possible prefixes. -// -// Example usage: -// -// PA_FORWARD_DECLARE_TEST(MyClassTest, TestMethod); -// -// namespace foo { -// class MyClass { -// private: -// PA_FRIEND_TEST_ALL_PREFIXES(::MyClassTest, TestMethod); // NOTE use of :: -// bool private_var; -// }; -// } // namespace foo -// -// class MyClassTest::TestMethod() { -// foo::MyClass foo_class; -// foo_class.private_var = true; -// } - -#define PA_FORWARD_DECLARE_TEST(test_case_name, test_name) \ - class test_case_name##_##test_name##_Test; \ - class test_case_name##_##DISABLED_##test_name##_Test; \ - class test_case_name##_##FLAKY_##test_name##_Test - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_GTEST_PROD_UTIL_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h b/base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h deleted file mode 100644 index 58f8cb6..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h +++ /dev/null
@@ -1,162 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_IMMEDIATE_CRASH_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_IMMEDIATE_CRASH_H_ - -#include "build/build_config.h" - -// Crashes in the fastest possible way with no attempt at logging. -// There are several constraints; see http://crbug.com/664209 for more context. -// -// - PA_TRAP_SEQUENCE_() must be fatal. It should not be possible to ignore the -// resulting exception or simply hit 'continue' to skip over it in a debugger. -// - Different instances of PA_TRAP_SEQUENCE_() must not be folded together, to -// ensure crash reports are debuggable. Unlike __builtin_trap(), asm volatile -// blocks will not be folded together. -// Note: PA_TRAP_SEQUENCE_() previously required an instruction with a unique -// nonce since unlike clang, GCC folds together identical asm volatile -// blocks. -// - PA_TRAP_SEQUENCE_() must produce a signal that is distinct from an invalid -// memory access. -// - PA_TRAP_SEQUENCE_() must be treated as a set of noreturn instructions. -// __builtin_unreachable() is used to provide that hint here. clang also uses -// this as a heuristic to pack the instructions in the function epilogue to -// improve code density. -// -// Additional properties that are nice to have: -// - PA_TRAP_SEQUENCE_() should be as compact as possible. -// - The first instruction of PA_TRAP_SEQUENCE_() should not change, to avoid -// shifting crash reporting clusters. As a consequence of this, explicit -// assembly is preferred over intrinsics. -// Note: this last bullet point may no longer be true, and may be removed in -// the future. - -// Note: PA_TRAP_SEQUENCE Is currently split into two macro helpers due to the -// fact that clang emits an actual instruction for __builtin_unreachable() on -// certain platforms (see https://crbug.com/958675). In addition, the -// int3/bkpt/brk will be removed in followups, so splitting it up like this now -// makes it easy to land the followups. - -#if defined(COMPILER_GCC) - -#if defined(ARCH_CPU_X86_FAMILY) - -// TODO(https://crbug.com/958675): In theory, it should be possible to use just -// int3. However, there are a number of crashes with SIGILL as the exception -// code, so it seems likely that there's a signal handler that allows execution -// to continue after SIGTRAP. -#define PA_TRAP_SEQUENCE1_() asm volatile("int3") - -#if BUILDFLAG(IS_APPLE) -// Intentionally empty: __builtin_unreachable() is always part of the sequence -// (see PA_IMMEDIATE_CRASH below) and already emits a ud2 on Mac. -#define PA_TRAP_SEQUENCE2_() asm volatile("") -#else -#define PA_TRAP_SEQUENCE2_() asm volatile("ud2") -#endif // BUILDFLAG(IS_APPLE) - -#elif defined(ARCH_CPU_ARMEL) - -// bkpt will generate a SIGBUS when running on armv7 and a SIGTRAP when running -// as a 32 bit userspace app on arm64. There doesn't seem to be any way to -// cause a SIGTRAP from userspace without using a syscall (which would be a -// problem for sandboxing). -// TODO(https://crbug.com/958675): Remove bkpt from this sequence. -#define PA_TRAP_SEQUENCE1_() asm volatile("bkpt #0") -#define PA_TRAP_SEQUENCE2_() asm volatile("udf #0") - -#elif defined(ARCH_CPU_ARM64) - -// This will always generate a SIGTRAP on arm64. -// TODO(https://crbug.com/958675): Remove brk from this sequence. -#define PA_TRAP_SEQUENCE1_() asm volatile("brk #0") -#define PA_TRAP_SEQUENCE2_() asm volatile("hlt #0") - -#else - -// Crash report accuracy will not be guaranteed on other architectures, but at -// least this will crash as expected. -#define PA_TRAP_SEQUENCE1_() __builtin_trap() -#define PA_TRAP_SEQUENCE2_() asm volatile("") - -#endif // ARCH_CPU_* - -#elif defined(COMPILER_MSVC) - -#if !defined(__clang__) - -// MSVC x64 doesn't support inline asm, so use the MSVC intrinsic. -#define PA_TRAP_SEQUENCE1_() __debugbreak() -#define PA_TRAP_SEQUENCE2_() - -#elif defined(ARCH_CPU_ARM64) - -// Windows ARM64 uses "BRK #F000" as its breakpoint instruction, and -// __debugbreak() generates that in both VC++ and clang. -#define PA_TRAP_SEQUENCE1_() __debugbreak() -// Intentionally empty: __builtin_unreachable() is always part of the sequence -// (see PA_IMMEDIATE_CRASH below) and already emits a ud2 on Win64, -// https://crbug.com/958373 -#define PA_TRAP_SEQUENCE2_() __asm volatile("") - -#else - -#define PA_TRAP_SEQUENCE1_() asm volatile("int3") -#define PA_TRAP_SEQUENCE2_() asm volatile("ud2") - -#endif // __clang__ - -#else - -#error No supported trap sequence! - -#endif // COMPILER_GCC - -#define PA_TRAP_SEQUENCE_() \ - do { \ - PA_TRAP_SEQUENCE1_(); \ - PA_TRAP_SEQUENCE2_(); \ - } while (false) - -// CHECK() and the trap sequence can be invoked from a constexpr function. -// This could make compilation fail on GCC, as it forbids directly using inline -// asm inside a constexpr function. However, it allows calling a lambda -// expression including the same asm. -// The side effect is that the top of the stacktrace will not point to the -// calling function, but to this anonymous lambda. This is still useful as the -// full name of the lambda will typically include the name of the function that -// calls CHECK() and the debugger will still break at the right line of code. -#if !defined(COMPILER_GCC) || defined(__clang__) - -#define PA_WRAPPED_TRAP_SEQUENCE_() PA_TRAP_SEQUENCE_() - -#else - -#define PA_WRAPPED_TRAP_SEQUENCE_() \ - do { \ - [] { PA_TRAP_SEQUENCE_(); }(); \ - } while (false) - -#endif // !defined(COMPILER_GCC) || defined(__clang__) - -#if defined(__clang__) || defined(COMPILER_GCC) - -// __builtin_unreachable() hints to the compiler that this is noreturn and can -// be packed in the function epilogue. -#define PA_IMMEDIATE_CRASH() \ - ({ \ - PA_WRAPPED_TRAP_SEQUENCE_(); \ - __builtin_unreachable(); \ - }) - -#else - -// This is supporting non-chromium user of logging.h to build with MSVC, like -// pdfium. On MSVC there is no __builtin_unreachable(). -#define PA_IMMEDIATE_CRASH() PA_WRAPPED_TRAP_SEQUENCE_() - -#endif // defined(__clang__) || defined(COMPILER_GCC) - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_IMMEDIATE_CRASH_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/ios/ios_util.h b/base/allocator/partition_allocator/partition_alloc_base/ios/ios_util.h deleted file mode 100644 index d855866..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/ios/ios_util.h +++ /dev/null
@@ -1,36 +0,0 @@ -// Copyright 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_IOS_IOS_UTIL_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_IOS_IOS_UTIL_H_ - -#include <stdint.h> - -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" - -namespace partition_alloc::internal::base::ios { - -// Returns whether the operating system is iOS 12 or later. -// TODO(crbug.com/1129482): Remove once minimum supported version is at least 12 -PA_COMPONENT_EXPORT(PARTITION_ALLOC) bool IsRunningOnIOS12OrLater(); - -// Returns whether the operating system is iOS 13 or later. -// TODO(crbug.com/1129483): Remove once minimum supported version is at least 13 -PA_COMPONENT_EXPORT(PARTITION_ALLOC) bool IsRunningOnIOS13OrLater(); - -// Returns whether the operating system is iOS 14 or later. -// TODO(crbug.com/1129484): Remove once minimum supported version is at least 14 -PA_COMPONENT_EXPORT(PARTITION_ALLOC) bool IsRunningOnIOS14OrLater(); - -// Returns whether the operating system is iOS 15 or later. -// TODO(crbug.com/1227419): Remove once minimum supported version is at least 15 -PA_COMPONENT_EXPORT(PARTITION_ALLOC) bool IsRunningOnIOS15OrLater(); - -// Returns whether the operating system is at the given version or later. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -bool IsRunningOnOrLater(int32_t major, int32_t minor, int32_t bug_fix); - -} // namespace partition_alloc::internal::base::ios - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_IOS_IOS_UTIL_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/ios/ios_util.mm b/base/allocator/partition_allocator/partition_alloc_base/ios/ios_util.mm deleted file mode 100644 index d5d607d..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/ios/ios_util.mm +++ /dev/null
@@ -1,57 +0,0 @@ -// Copyright 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/ios/ios_util.h" - -#include <array> - -#include "base/allocator/partition_allocator/partition_alloc_base/system/sys_info.h" - -namespace partition_alloc::internal::base::ios { - -bool IsRunningOnIOS12OrLater() { - static const bool is_running_on_or_later = IsRunningOnOrLater(12, 0, 0); - return is_running_on_or_later; -} - -bool IsRunningOnIOS13OrLater() { - static const bool is_running_on_or_later = IsRunningOnOrLater(13, 0, 0); - return is_running_on_or_later; -} - -bool IsRunningOnIOS14OrLater() { - static const bool is_running_on_or_later = IsRunningOnOrLater(14, 0, 0); - return is_running_on_or_later; -} - -bool IsRunningOnIOS15OrLater() { - static const bool is_running_on_or_later = IsRunningOnOrLater(15, 0, 0); - return is_running_on_or_later; -} - -bool IsRunningOnOrLater(int32_t major, int32_t minor, int32_t bug_fix) { - static const class OSVersion { - public: - OSVersion() { - SysInfo::OperatingSystemVersionNumbers( - ¤t_version_[0], ¤t_version_[1], ¤t_version_[2]); - } - - bool IsRunningOnOrLater(int32_t version[3]) const { - for (size_t i = 0; i < std::size(current_version_); ++i) { - if (current_version_[i] != version[i]) - return current_version_[i] > version[i]; - } - return true; - } - - private: - int32_t current_version_[3]; - } kOSVersion; - - int32_t version[3] = {major, minor, bug_fix}; - return kOSVersion.IsRunningOnOrLater(version); -} - -} // namespace partition_alloc::internal::base::ios
diff --git a/base/allocator/partition_allocator/partition_alloc_base/logging.cc b/base/allocator/partition_allocator/partition_alloc_base/logging.cc deleted file mode 100644 index 464a352..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/logging.cc +++ /dev/null
@@ -1,284 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/logging.h" - -// TODO(1151236): After finishing copying //base files to PA library, remove -// defined(BASE_CHECK_H_) from here. -#if defined( \ - BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_CHECK_H_) || \ - defined(BASE_CHECK_H_) || \ - defined(BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_CHECK_H_) -#error "logging.h should not include check.h" -#endif - -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/alias.h" -#include "base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h" -#include "base/allocator/partition_allocator/partition_alloc_base/strings/stringprintf.h" -#include "build/build_config.h" - -#if BUILDFLAG(IS_WIN) - -#include <io.h> -#include <windows.h> -// Windows warns on using write(). It prefers _write(). -#define write(fd, buf, count) _write(fd, buf, static_cast<unsigned int>(count)) -// Windows doesn't define STDERR_FILENO. Define it here. -#define STDERR_FILENO 2 - -#endif - -#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#endif - -#include <cstring> -#include <ostream> -#include <string> - -#include "base/allocator/partition_allocator/partition_alloc_base/posix/eintr_wrapper.h" - -#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) -#include "base/allocator/partition_allocator/partition_alloc_base/posix/safe_strerror.h" -#endif - -namespace partition_alloc::internal::logging { - -namespace { - -const char* const log_severity_names[] = {"INFO", "WARNING", "ERROR", "FATAL"}; -static_assert(LOGGING_NUM_SEVERITIES == std::size(log_severity_names), - "Incorrect number of log_severity_names"); - -const char* log_severity_name(int severity) { - if (severity >= 0 && severity < LOGGING_NUM_SEVERITIES) - return log_severity_names[severity]; - return "UNKNOWN"; -} - -int g_min_log_level = 0; - -// A log message handler that gets notified of every log message we process. -LogMessageHandlerFunction g_log_message_handler = nullptr; - -#if !BUILDFLAG(IS_WIN) -void WriteToStderr(const char* data, size_t length) { - size_t bytes_written = 0; - int rv; - while (bytes_written < length) { - rv = PA_HANDLE_EINTR( - write(STDERR_FILENO, data + bytes_written, length - bytes_written)); - if (rv < 0) { - // Give up, nothing we can do now. - break; - } - bytes_written += rv; - } -} -#else // !BUILDFLAG(IS_WIN) -void WriteToStderr(const char* data, size_t length) { - HANDLE handle = ::GetStdHandle(STD_ERROR_HANDLE); - const char* ptr = data; - const char* ptr_end = data + length; - while (ptr < ptr_end) { - DWORD bytes_written = 0; - if (!::WriteFile(handle, ptr, ptr_end - ptr, &bytes_written, nullptr) || - bytes_written == 0) { - // Give up, nothing we can do now. - break; - } - ptr += bytes_written; - } -} -#endif // !BUILDFLAG(IS_WIN) - -} // namespace - -#if BUILDFLAG(PA_DCHECK_IS_CONFIGURABLE) -// In DCHECK-enabled Chrome builds, allow the meaning of LOGGING_DCHECK to be -// determined at run-time. We default it to INFO, to avoid it triggering -// crashes before the run-time has explicitly chosen the behaviour. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -logging::LogSeverity LOGGING_DCHECK = LOGGING_INFO; -#endif // BUILDFLAG(PA_DCHECK_IS_CONFIGURABLE) - -// This is never instantiated, it's just used for EAT_STREAM_PARAMETERS to have -// an object of the correct type on the LHS of the unused part of the ternary -// operator. -std::ostream* g_swallow_stream; - -void SetMinLogLevel(int level) { - g_min_log_level = std::min(LOGGING_FATAL, level); -} - -int GetMinLogLevel() { - return g_min_log_level; -} - -bool ShouldCreateLogMessage(int severity) { - if (severity < g_min_log_level) - return false; - - // Return true here unless we know ~LogMessage won't do anything. - return true; -} - -int GetVlogVerbosity() { - return std::max(-1, LOG_INFO - GetMinLogLevel()); -} - -void SetLogMessageHandler(LogMessageHandlerFunction handler) { - g_log_message_handler = handler; -} - -LogMessageHandlerFunction GetLogMessageHandler() { - return g_log_message_handler; -} - -LogMessage::LogMessage(const char* file, int line, LogSeverity severity) - : severity_(severity), file_(file), line_(line) { - Init(file, line); -} - -LogMessage::LogMessage(const char* file, int line, const char* condition) - : severity_(LOGGING_FATAL), file_(file), line_(line) { - Init(file, line); - stream_ << "Check failed: " << condition << ". "; -} - -LogMessage::~LogMessage() { - stream_ << std::endl; - std::string str_newline(stream_.str()); - - // Give any log message handler first dibs on the message. - if (g_log_message_handler && - g_log_message_handler(severity_, file_, line_, message_start_, - str_newline)) { - // The handler took care of it, no further processing. - return; - } - - // Always use RawLog() if g_log_message_handler doesn't filter messages. - RawLog(severity_, str_newline.c_str()); -} - -// writes the common header info to the stream -void LogMessage::Init(const char* file, int line) { - std::string filename(file); - size_t last_slash_pos = filename.find_last_of("\\/"); - if (last_slash_pos != std::string::npos) - filename.erase(0, last_slash_pos + 1); - - { - // TODO(darin): It might be nice if the columns were fixed width. - stream_ << '['; - // TODO(1151236): show process id, thread id, timestamp and so on - // if needed. - if (severity_ >= 0) { - stream_ << log_severity_name(severity_); - } else { - stream_ << "VERBOSE" << -severity_; - } - stream_ << ":" << filename << "(" << line << ")] "; - } - message_start_ = stream_.str().length(); -} - -#if BUILDFLAG(IS_WIN) -// This has already been defined in the header, but defining it again as DWORD -// ensures that the type used in the header is equivalent to DWORD. If not, -// the redefinition is a compile error. -typedef DWORD SystemErrorCode; -#endif - -SystemErrorCode GetLastSystemErrorCode() { -#if BUILDFLAG(IS_WIN) - return ::GetLastError(); -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) - return errno; -#endif -} - -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -std::string SystemErrorCodeToString(SystemErrorCode error_code) { -#if BUILDFLAG(IS_WIN) - const int kErrorMessageBufferSize = 256; - char msgbuf[kErrorMessageBufferSize]; - DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; - DWORD len = FormatMessageA(flags, nullptr, error_code, 0, msgbuf, - std::size(msgbuf), nullptr); - if (len) { - // Messages returned by system end with line breaks. - std::string message(msgbuf); - size_t whitespace_pos = message.find_last_not_of("\n\r "); - if (whitespace_pos != std::string::npos) - message.erase(whitespace_pos + 1); - return message + base::TruncatingStringPrintf(" (0x%lX)", error_code); - } - return base::TruncatingStringPrintf( - "Error (0x%lX) while retrieving error. (0x%lX)", GetLastError(), - error_code); -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) - return base::safe_strerror(error_code) + - base::TruncatingStringPrintf(" (%d)", error_code); -#endif // BUILDFLAG(IS_WIN) -} - -#if BUILDFLAG(IS_WIN) -Win32ErrorLogMessage::Win32ErrorLogMessage(const char* file, - int line, - LogSeverity severity, - SystemErrorCode err) - : LogMessage(file, line, severity), err_(err) {} - -Win32ErrorLogMessage::~Win32ErrorLogMessage() { - stream() << ": " << SystemErrorCodeToString(err_); - // We're about to crash (CHECK). Put |err_| on the stack (by placing it in a - // field) and use Alias in hopes that it makes it into crash dumps. - DWORD last_error = err_; - base::debug::Alias(&last_error); -} -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) -ErrnoLogMessage::ErrnoLogMessage(const char* file, - int line, - LogSeverity severity, - SystemErrorCode err) - : LogMessage(file, line, severity), err_(err) {} - -ErrnoLogMessage::~ErrnoLogMessage() { - stream() << ": " << SystemErrorCodeToString(err_); - // We're about to crash (CHECK). Put |err_| on the stack (by placing it in a - // field) and use Alias in hopes that it makes it into crash dumps. - int last_error = err_; - base::debug::Alias(&last_error); -} -#endif // BUILDFLAG(IS_WIN) - -void RawLog(int level, const char* message) { - if (level >= g_min_log_level && message) { -#if !BUILDFLAG(IS_WIN) - const size_t message_len = strlen(message); -#else // !BUILDFLAG(IS_WIN) - const size_t message_len = ::lstrlenA(message); -#endif // !BUILDFLAG(IS_WIN) - WriteToStderr(message, message_len); - - if (message_len > 0 && message[message_len - 1] != '\n') { - WriteToStderr("\n", 1); - } - } - - if (level == LOGGING_FATAL) - PA_IMMEDIATE_CRASH(); -} - -// This was defined at the beginning of this file. -#undef write - -} // namespace partition_alloc::internal::logging
diff --git a/base/allocator/partition_allocator/partition_alloc_base/logging.h b/base/allocator/partition_allocator/partition_alloc_base/logging.h deleted file mode 100644 index 270d770..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/logging.h +++ /dev/null
@@ -1,522 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_LOGGING_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_LOGGING_H_ - -#include <stddef.h> - -#include <cassert> -#include <cstdint> -#include <sstream> -#include <string> - -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_base/scoped_clear_last_error.h" -#include "build/build_config.h" - -// TODO(1151236): Need to update the description, because logging for PA -// standalone library was minimized. -// -// Optional message capabilities -// ----------------------------- -// Assertion failed messages and fatal errors are displayed in a dialog box -// before the application exits. However, running this UI creates a message -// loop, which causes application messages to be processed and potentially -// dispatched to existing application windows. Since the application is in a -// bad state when this assertion dialog is displayed, these messages may not -// get processed and hang the dialog, or the application might go crazy. -// -// Therefore, it can be beneficial to display the error dialog in a separate -// process from the main application. When the logging system needs to display -// a fatal error dialog box, it will look for a program called -// "DebugMessage.exe" in the same directory as the application executable. It -// will run this application with the message as the command line, and will -// not include the name of the application as is traditional for easier -// parsing. -// -// The code for DebugMessage.exe is only one line. In WinMain, do: -// MessageBox(NULL, GetCommandLineW(), L"Fatal Error", 0); -// -// If DebugMessage.exe is not found, the logging code will use a normal -// MessageBox, potentially causing the problems discussed above. - -// Instructions -// ------------ -// -// Make a bunch of macros for logging. The way to log things is to stream -// things to PA_LOG(<a particular severity level>). E.g., -// -// PA_LOG(INFO) << "Found " << num_cookies << " cookies"; -// -// You can also do conditional logging: -// -// PA_LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; -// -// The CHECK(condition) macro is active in both debug and release builds and -// effectively performs a PA_LOG(FATAL) which terminates the process and -// generates a crashdump unless a debugger is attached. -// -// There are also "debug mode" logging macros like the ones above: -// -// PA_DLOG(INFO) << "Found cookies"; -// -// PA_DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; -// -// All "debug mode" logging is compiled away to nothing for non-debug mode -// compiles. PA_LOG_IF and development flags also work well together -// because the code can be compiled away sometimes. -// -// We also have -// -// PA_LOG_ASSERT(assertion); -// PA_DLOG_ASSERT(assertion); -// -// which is syntactic sugar for PA_{,D}LOG_IF(FATAL, assert fails) << assertion; -// -// There are "verbose level" logging macros. They look like -// -// PA_VLOG(1) << "I'm printed when you run the program with --v=1 or more"; -// PA_VLOG(2) << "I'm printed when you run the program with --v=2 or more"; -// -// These always log at the INFO log level (when they log at all). -// -// There's also PA_VLOG_IS_ON(n) "verbose level" condition macro. To be used as -// -// if (PA_VLOG_IS_ON(2)) { -// // do some logging preparation and logging -// // that can't be accomplished with just PA_VLOG(2) << ...; -// } -// -// There is also a PA_VLOG_IF "verbose level" condition macro for sample -// cases, when some extra computation and preparation for logs is not -// needed. -// -// PA_VLOG_IF(1, (size > 1024)) -// << "I'm printed when size is more than 1024 and when you run the " -// "program with --v=1 or more"; -// -// We also override the standard 'assert' to use 'PA_DLOG_ASSERT'. -// -// Lastly, there is: -// -// PA_PLOG(ERROR) << "Couldn't do foo"; -// PA_DPLOG(ERROR) << "Couldn't do foo"; -// PA_PLOG_IF(ERROR, cond) << "Couldn't do foo"; -// PA_DPLOG_IF(ERROR, cond) << "Couldn't do foo"; -// PA_PCHECK(condition) << "Couldn't do foo"; -// PA_DPCHECK(condition) << "Couldn't do foo"; -// -// which append the last system error to the message in string form (taken from -// GetLastError() on Windows and errno on POSIX). -// -// The supported severity levels for macros that allow you to specify one -// are (in increasing order of severity) INFO, WARNING, ERROR, and FATAL. -// -// Very important: logging a message at the FATAL severity level causes -// the program to terminate (after the message is logged). -// -// There is the special severity of DFATAL, which logs FATAL in DCHECK-enabled -// builds, ERROR in normal mode. -// -// Output is formatted as per the following example: -// [VERBOSE1:drm_device_handle.cc(90)] Succeeded -// authenticating /dev/dri/card0 in 0 ms with 1 attempt(s) -// -// The colon separated fields inside the brackets are as follows: -// 1. The log level -// 2. The filename and line number where the log was instantiated -// -// Additional logging-related information can be found here: -// https://chromium.googlesource.com/chromium/src/+/main/docs/linux/debugging.md#Logging - -namespace partition_alloc::internal::logging { - -// Sets the log level. Anything at or above this level will be written to the -// log file/displayed to the user (if applicable). Anything below this level -// will be silently ignored. The log level defaults to 0 (everything is logged -// up to level INFO) if this function is not called. -// Note that log messages for VLOG(x) are logged at level -x, so setting -// the min log level to negative values enables verbose logging. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) void SetMinLogLevel(int level); - -// Gets the current log level. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) int GetMinLogLevel(); - -// Used by PA_LOG_IS_ON to lazy-evaluate stream arguments. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) bool ShouldCreateLogMessage(int severity); - -// Gets the PA_VLOG default verbosity level. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) int GetVlogVerbosity(); - -// Sets the Log Message Handler that gets passed every log message before -// it's sent to other log destinations (if any). -// Returns true to signal that it handled the message and the message -// should not be sent to other log destinations. -typedef bool (*LogMessageHandlerFunction)(int severity, - const char* file, - int line, - size_t message_start, - const std::string& str); -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -void SetLogMessageHandler(LogMessageHandlerFunction handler); -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -LogMessageHandlerFunction GetLogMessageHandler(); - -using LogSeverity = int; -constexpr LogSeverity LOGGING_VERBOSE = -1; // This is level 1 verbosity -// Note: the log severities are used to index into the array of names, -// see log_severity_names. -constexpr LogSeverity LOGGING_INFO = 0; -constexpr LogSeverity LOGGING_WARNING = 1; -constexpr LogSeverity LOGGING_ERROR = 2; -constexpr LogSeverity LOGGING_FATAL = 3; -constexpr LogSeverity LOGGING_NUM_SEVERITIES = 4; - -// LOGGING_DFATAL is LOGGING_FATAL in DCHECK-enabled builds, ERROR in normal -// mode. -#if BUILDFLAG(PA_DCHECK_IS_ON) -constexpr LogSeverity LOGGING_DFATAL = LOGGING_FATAL; -#else -constexpr LogSeverity LOGGING_DFATAL = LOGGING_ERROR; -#endif - -// This block duplicates the above entries to facilitate incremental conversion -// from LOG_FOO to LOGGING_FOO. -// TODO(thestig): Convert existing users to LOGGING_FOO and remove this block. -constexpr LogSeverity LOG_VERBOSE = LOGGING_VERBOSE; -constexpr LogSeverity LOG_INFO = LOGGING_INFO; -constexpr LogSeverity LOG_WARNING = LOGGING_WARNING; -constexpr LogSeverity LOG_ERROR = LOGGING_ERROR; -constexpr LogSeverity LOG_FATAL = LOGGING_FATAL; -constexpr LogSeverity LOG_DFATAL = LOGGING_DFATAL; - -// A few definitions of macros that don't generate much code. These are used -// by PA_LOG() and LOG_IF, etc. Since these are used all over our code, it's -// better to have compact code for these operations. -#define PA_COMPACT_GOOGLE_LOG_EX_INFO(ClassName, ...) \ - ::partition_alloc::internal::logging::ClassName( \ - __FILE__, __LINE__, ::partition_alloc::internal::logging::LOGGING_INFO, \ - ##__VA_ARGS__) -#define PA_COMPACT_GOOGLE_LOG_EX_WARNING(ClassName, ...) \ - ::partition_alloc::internal::logging::ClassName( \ - __FILE__, __LINE__, \ - ::partition_alloc::internal::logging::LOGGING_WARNING, ##__VA_ARGS__) -#define PA_COMPACT_GOOGLE_LOG_EX_ERROR(ClassName, ...) \ - ::partition_alloc::internal::logging::ClassName( \ - __FILE__, __LINE__, ::partition_alloc::internal::logging::LOGGING_ERROR, \ - ##__VA_ARGS__) -#define PA_COMPACT_GOOGLE_LOG_EX_FATAL(ClassName, ...) \ - ::partition_alloc::internal::logging::ClassName( \ - __FILE__, __LINE__, ::partition_alloc::internal::logging::LOGGING_FATAL, \ - ##__VA_ARGS__) -#define PA_COMPACT_GOOGLE_LOG_EX_DFATAL(ClassName, ...) \ - ::partition_alloc::internal::logging::ClassName( \ - __FILE__, __LINE__, \ - ::partition_alloc::internal::logging::LOGGING_DFATAL, ##__VA_ARGS__) -#define PA_COMPACT_GOOGLE_LOG_EX_DCHECK(ClassName, ...) \ - ::partition_alloc::internal::logging::ClassName( \ - __FILE__, __LINE__, \ - ::partition_alloc::internal::logging::LOGGING_DCHECK, ##__VA_ARGS__) - -#define PA_COMPACT_GOOGLE_LOG_INFO PA_COMPACT_GOOGLE_LOG_EX_INFO(LogMessage) -#define PA_COMPACT_GOOGLE_LOG_WARNING \ - PA_COMPACT_GOOGLE_LOG_EX_WARNING(LogMessage) -#define PA_COMPACT_GOOGLE_LOG_ERROR PA_COMPACT_GOOGLE_LOG_EX_ERROR(LogMessage) -#define PA_COMPACT_GOOGLE_LOG_FATAL PA_COMPACT_GOOGLE_LOG_EX_FATAL(LogMessage) -#define PA_COMPACT_GOOGLE_LOG_DFATAL PA_COMPACT_GOOGLE_LOG_EX_DFATAL(LogMessage) -#define PA_COMPACT_GOOGLE_LOG_DCHECK PA_COMPACT_GOOGLE_LOG_EX_DCHECK(LogMessage) - -#if BUILDFLAG(IS_WIN) -// wingdi.h defines ERROR to be 0. When we call PA_LOG(ERROR), it gets -// substituted with 0, and it expands to PA_COMPACT_GOOGLE_LOG_0. To allow us -// to keep using this syntax, we define this macro to do the same thing -// as PA_COMPACT_GOOGLE_LOG_ERROR, and also define ERROR the same way that -// the Windows SDK does for consistency. -#define PA_ERROR 0 -#define PA_COMPACT_GOOGLE_LOG_EX_0(ClassName, ...) \ - PA_COMPACT_GOOGLE_LOG_EX_ERROR(ClassName, ##__VA_ARGS__) -#define PA_COMPACT_GOOGLE_LOG_0 PA_COMPACT_GOOGLE_LOG_ERROR -// Needed for LOG_IS_ON(ERROR). -constexpr LogSeverity LOGGING_0 = LOGGING_ERROR; -#endif - -// As special cases, we can assume that LOG_IS_ON(FATAL) always holds. Also, -// LOG_IS_ON(DFATAL) always holds in debug mode. In particular, CHECK()s will -// always fire if they fail. -#define PA_LOG_IS_ON(severity) \ - (::partition_alloc::internal::logging::ShouldCreateLogMessage( \ - ::partition_alloc::internal::logging::LOGGING_##severity)) - -// We don't do any caching tricks with VLOG_IS_ON() like the -// google-glog version since it increases binary size. This means -// that using the v-logging functions in conjunction with --vmodule -// may be slow. -#define PA_VLOG_IS_ON(verboselevel) \ - ((verboselevel) <= ::partition_alloc::internal::logging::GetVlogVerbosity()) - -// Helper macro which avoids evaluating the arguments to a stream if -// the condition doesn't hold. Condition is evaluated once and only once. -#define PA_LAZY_STREAM(stream, condition) \ - !(condition) \ - ? (void)0 \ - : ::partition_alloc::internal::logging::LogMessageVoidify() & (stream) - -// We use the preprocessor's merging operator, "##", so that, e.g., -// PA_LOG(INFO) becomes the token PA_COMPACT_GOOGLE_LOG_INFO. There's some -// funny subtle difference between ostream member streaming functions (e.g., -// ostream::operator<<(int) and ostream non-member streaming functions -// (e.g., ::operator<<(ostream&, string&): it turns out that it's -// impossible to stream something like a string directly to an unnamed -// ostream. We employ a neat hack by calling the stream() member -// function of LogMessage which seems to avoid the problem. -#define PA_LOG_STREAM(severity) PA_COMPACT_GOOGLE_LOG_##severity.stream() - -#define PA_LOG(severity) \ - PA_LAZY_STREAM(PA_LOG_STREAM(severity), PA_LOG_IS_ON(severity)) -#define PA_LOG_IF(severity, condition) \ - PA_LAZY_STREAM(PA_LOG_STREAM(severity), PA_LOG_IS_ON(severity) && (condition)) - -// The VLOG macros log with negative verbosities. -#define PA_VLOG_STREAM(verbose_level) \ - ::partition_alloc::internal::logging::LogMessage(__FILE__, __LINE__, \ - -(verbose_level)) \ - .stream() - -#define PA_VLOG(verbose_level) \ - PA_LAZY_STREAM(PA_VLOG_STREAM(verbose_level), PA_VLOG_IS_ON(verbose_level)) - -#define PA_VLOG_IF(verbose_level, condition) \ - PA_LAZY_STREAM(PA_VLOG_STREAM(verbose_level), \ - PA_VLOG_IS_ON(verbose_level) && (condition)) - -#if BUILDFLAG(IS_WIN) -#define PA_VPLOG_STREAM(verbose_level) \ - ::partition_alloc::internal::logging::Win32ErrorLogMessage( \ - __FILE__, __LINE__, -(verbose_level), \ - ::partition_alloc::internal::logging::GetLastSystemErrorCode()) \ - .stream() -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) -#define PA_VPLOG_STREAM(verbose_level) \ - ::partition_alloc::internal::logging::ErrnoLogMessage( \ - __FILE__, __LINE__, -(verbose_level), \ - ::partition_alloc::internal::logging::GetLastSystemErrorCode()) \ - .stream() -#endif - -#define PA_VPLOG(verbose_level) \ - PA_LAZY_STREAM(PA_VPLOG_STREAM(verbose_level), PA_VLOG_IS_ON(verbose_level)) - -#define PA_VPLOG_IF(verbose_level, condition) \ - PA_LAZY_STREAM(PA_VPLOG_STREAM(verbose_level), \ - PA_VLOG_IS_ON(verbose_level) && (condition)) - -// TODO(akalin): Add more VLOG variants, e.g. VPLOG. - -#define PA_LOG_ASSERT(condition) \ - PA_LOG_IF(FATAL, !(PA_ANALYZER_ASSUME_TRUE(condition))) \ - << "Assert failed: " #condition ". " - -#if BUILDFLAG(IS_WIN) -#define PA_PLOG_STREAM(severity) \ - PA_COMPACT_GOOGLE_LOG_EX_##severity( \ - Win32ErrorLogMessage, \ - ::partition_alloc::internal::logging::GetLastSystemErrorCode()) \ - .stream() -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) -#define PA_PLOG_STREAM(severity) \ - PA_COMPACT_GOOGLE_LOG_EX_##severity( \ - ErrnoLogMessage, \ - ::partition_alloc::internal::logging::GetLastSystemErrorCode()) \ - .stream() -#endif - -#define PA_PLOG(severity) \ - PA_LAZY_STREAM(PA_PLOG_STREAM(severity), PA_LOG_IS_ON(severity)) - -#define PA_PLOG_IF(severity, condition) \ - PA_LAZY_STREAM(PA_PLOG_STREAM(severity), \ - PA_LOG_IS_ON(severity) && (condition)) - -PA_COMPONENT_EXPORT(PARTITION_ALLOC) extern std::ostream* g_swallow_stream; - -// Note that g_swallow_stream is used instead of an arbitrary PA_LOG() stream to -// avoid the creation of an object with a non-trivial destructor (LogMessage). -// On MSVC x86 (checked on 2015 Update 3), this causes a few additional -// pointless instructions to be emitted even at full optimization level, even -// though the : arm of the ternary operator is clearly never executed. Using a -// simpler object to be &'d with Voidify() avoids these extra instructions. -// Using a simpler POD object with a templated operator<< also works to avoid -// these instructions. However, this causes warnings on statically defined -// implementations of operator<<(std::ostream, ...) in some .cc files, because -// they become defined-but-unreferenced functions. A reinterpret_cast of 0 to an -// ostream* also is not suitable, because some compilers warn of undefined -// behavior. -#define PA_EAT_STREAM_PARAMETERS \ - true ? (void)0 \ - : ::partition_alloc::internal::logging::LogMessageVoidify() & \ - (*::partition_alloc::internal::logging::g_swallow_stream) - -// Definitions for DLOG et al. - -#if BUILDFLAG(PA_DCHECK_IS_ON) - -#define PA_DLOG_IS_ON(severity) PA_LOG_IS_ON(severity) -#define PA_DLOG_IF(severity, condition) PA_LOG_IF(severity, condition) -#define PA_DLOG_ASSERT(condition) PA_LOG_ASSERT(condition) -#define PA_DPLOG_IF(severity, condition) PA_PLOG_IF(severity, condition) -#define PA_DVLOG_IF(verboselevel, condition) PA_VLOG_IF(verboselevel, condition) -#define PA_DVPLOG_IF(verboselevel, condition) \ - PA_VPLOG_IF(verboselevel, condition) - -#else // BUILDFLAG(PA_DCHECK_IS_ON) - -// If !BUILDFLAG(PA_DCHECK_IS_ON), we want to avoid emitting any references to -// |condition| (which may reference a variable defined only if -// BUILDFLAG(PA_DCHECK_IS_ON)). Contrast this with DCHECK et al., which has -// different behavior. - -#define PA_DLOG_IS_ON(severity) false -#define PA_DLOG_IF(severity, condition) PA_EAT_STREAM_PARAMETERS -#define PA_DLOG_ASSERT(condition) PA_EAT_STREAM_PARAMETERS -#define PA_DPLOG_IF(severity, condition) PA_EAT_STREAM_PARAMETERS -#define PA_DVLOG_IF(verboselevel, condition) PA_EAT_STREAM_PARAMETERS -#define PA_DVPLOG_IF(verboselevel, condition) PA_EAT_STREAM_PARAMETERS - -#endif // BUILDFLAG(PA_DCHECK_IS_ON) - -#define PA_DLOG(severity) \ - PA_LAZY_STREAM(PA_LOG_STREAM(severity), PA_DLOG_IS_ON(severity)) - -#define PA_DPLOG(severity) \ - PA_LAZY_STREAM(PA_PLOG_STREAM(severity), PA_DLOG_IS_ON(severity)) - -#define PA_DVLOG(verboselevel) PA_DVLOG_IF(verboselevel, true) - -#define PA_DVPLOG(verboselevel) PA_DVPLOG_IF(verboselevel, true) - -// Definitions for DCHECK et al. - -#if BUILDFLAG(PA_DCHECK_IS_CONFIGURABLE) -PA_COMPONENT_EXPORT(PARTITION_ALLOC) extern LogSeverity LOGGING_DCHECK; -#else -constexpr LogSeverity LOGGING_DCHECK = LOGGING_FATAL; -#endif // BUILDFLAG(PA_DCHECK_IS_CONFIGURABLE) - -// Redefine the standard assert to use our nice log files -#undef assert -#define assert(x) PA_DLOG_ASSERT(x) - -// This class more or less represents a particular log message. You -// create an instance of LogMessage and then stream stuff to it. -// When you finish streaming to it, ~LogMessage is called and the -// full message gets streamed to the appropriate destination. -// -// You shouldn't actually use LogMessage's constructor to log things, -// though. You should use the PA_LOG() macro (and variants thereof) -// above. -class PA_COMPONENT_EXPORT(PARTITION_ALLOC) LogMessage { - public: - // Used for PA_LOG(severity). - LogMessage(const char* file, int line, LogSeverity severity); - - // Used for CHECK(). Implied severity = LOGGING_FATAL. - LogMessage(const char* file, int line, const char* condition); - LogMessage(const LogMessage&) = delete; - LogMessage& operator=(const LogMessage&) = delete; - virtual ~LogMessage(); - - std::ostream& stream() { return stream_; } - - LogSeverity severity() { return severity_; } - std::string str() { return stream_.str(); } - - private: - void Init(const char* file, int line); - - const LogSeverity severity_; - std::ostringstream stream_; - size_t message_start_; // Offset of the start of the message (past prefix - // info). - // The file and line information passed in to the constructor. - const char* const file_; - const int line_; - - // This is useful since the LogMessage class uses a lot of Win32 calls - // that will lose the value of GLE and the code that called the log function - // will have lost the thread error value when the log call returns. - base::ScopedClearLastError last_error_; -}; - -// This class is used to explicitly ignore values in the conditional -// logging macros. This avoids compiler warnings like "value computed -// is not used" and "statement has no effect". -class LogMessageVoidify { - public: - LogMessageVoidify() = default; - // This has to be an operator with a precedence lower than << but - // higher than ?: - void operator&(std::ostream&) {} -}; - -#if BUILDFLAG(IS_WIN) -typedef unsigned long SystemErrorCode; -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) -typedef int SystemErrorCode; -#endif - -// Alias for ::GetLastError() on Windows and errno on POSIX. Avoids having to -// pull in windows.h just for GetLastError() and DWORD. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) SystemErrorCode GetLastSystemErrorCode(); -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -std::string SystemErrorCodeToString(SystemErrorCode error_code); - -#if BUILDFLAG(IS_WIN) -// Appends a formatted system message of the GetLastError() type. -class PA_COMPONENT_EXPORT(PARTITION_ALLOC) Win32ErrorLogMessage - : public LogMessage { - public: - Win32ErrorLogMessage(const char* file, - int line, - LogSeverity severity, - SystemErrorCode err); - Win32ErrorLogMessage(const Win32ErrorLogMessage&) = delete; - Win32ErrorLogMessage& operator=(const Win32ErrorLogMessage&) = delete; - // Appends the error message before destructing the encapsulated class. - ~Win32ErrorLogMessage() override; - - private: - SystemErrorCode err_; -}; -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) -// Appends a formatted system message of the errno type -class PA_COMPONENT_EXPORT(PARTITION_ALLOC) ErrnoLogMessage : public LogMessage { - public: - ErrnoLogMessage(const char* file, - int line, - LogSeverity severity, - SystemErrorCode err); - ErrnoLogMessage(const ErrnoLogMessage&) = delete; - ErrnoLogMessage& operator=(const ErrnoLogMessage&) = delete; - // Appends the error message before destructing the encapsulated class. - ~ErrnoLogMessage() override; - - private: - SystemErrorCode err_; -}; -#endif // BUILDFLAG(IS_WIN) - -// Async signal safe logging mechanism. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -void RawLog(int level, const char* message); - -#define PA_RAW_LOG(level, message) \ - ::partition_alloc::internal::logging::RawLog( \ - ::partition_alloc::internal::logging::LOGGING_##level, message) - -} // namespace partition_alloc::internal::logging - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_LOGGING_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/logging_pa_unittest.cc b/base/allocator/partition_allocator/partition_alloc_base/logging_pa_unittest.cc deleted file mode 100644 index 3cd2728..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/logging_pa_unittest.cc +++ /dev/null
@@ -1,148 +0,0 @@ -// Copyright 2011 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <sstream> -#include <string> - -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_base/logging.h" -#include "build/build_config.h" - -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace partition_alloc::internal::logging { - -namespace { - -using ::testing::_; -using ::testing::Return; - -class MockLogSource { - public: - MOCK_METHOD0(Log, const char*()); -}; - -TEST(PALoggingTest, BasicLogging) { - MockLogSource mock_log_source; - constexpr int kTimes = -#if BUILDFLAG(PA_DCHECK_IS_ON) - 16; -#else - 8; -#endif - EXPECT_CALL(mock_log_source, Log()) - .Times(kTimes) - .WillRepeatedly(Return("log message")); - - SetMinLogLevel(LOGGING_INFO); - - EXPECT_TRUE(PA_LOG_IS_ON(INFO)); - EXPECT_EQ(BUILDFLAG(PA_DCHECK_IS_ON), PA_DLOG_IS_ON(INFO)); - EXPECT_TRUE(PA_VLOG_IS_ON(0)); - - PA_LOG(INFO) << mock_log_source.Log(); - PA_LOG_IF(INFO, true) << mock_log_source.Log(); - PA_PLOG(INFO) << mock_log_source.Log(); - PA_PLOG_IF(INFO, true) << mock_log_source.Log(); - PA_VLOG(0) << mock_log_source.Log(); - PA_VLOG_IF(0, true) << mock_log_source.Log(); - PA_VPLOG(0) << mock_log_source.Log(); - PA_VPLOG_IF(0, true) << mock_log_source.Log(); - - PA_DLOG(INFO) << mock_log_source.Log(); - PA_DLOG_IF(INFO, true) << mock_log_source.Log(); - PA_DPLOG(INFO) << mock_log_source.Log(); - PA_DPLOG_IF(INFO, true) << mock_log_source.Log(); - PA_DVLOG(0) << mock_log_source.Log(); - PA_DVLOG_IF(0, true) << mock_log_source.Log(); - PA_DVPLOG(0) << mock_log_source.Log(); - PA_DVPLOG_IF(0, true) << mock_log_source.Log(); -} - -TEST(PALoggingTest, LogIsOn) { - SetMinLogLevel(LOGGING_INFO); - EXPECT_TRUE(PA_LOG_IS_ON(INFO)); - EXPECT_TRUE(PA_LOG_IS_ON(WARNING)); - EXPECT_TRUE(PA_LOG_IS_ON(ERROR)); - EXPECT_TRUE(PA_LOG_IS_ON(FATAL)); - EXPECT_TRUE(PA_LOG_IS_ON(DFATAL)); - - SetMinLogLevel(LOGGING_WARNING); - EXPECT_FALSE(PA_LOG_IS_ON(INFO)); - EXPECT_TRUE(PA_LOG_IS_ON(WARNING)); - EXPECT_TRUE(PA_LOG_IS_ON(ERROR)); - EXPECT_TRUE(PA_LOG_IS_ON(FATAL)); - EXPECT_TRUE(PA_LOG_IS_ON(DFATAL)); - - SetMinLogLevel(LOGGING_ERROR); - EXPECT_FALSE(PA_LOG_IS_ON(INFO)); - EXPECT_FALSE(PA_LOG_IS_ON(WARNING)); - EXPECT_TRUE(PA_LOG_IS_ON(ERROR)); - EXPECT_TRUE(PA_LOG_IS_ON(FATAL)); - EXPECT_TRUE(PA_LOG_IS_ON(DFATAL)); - - SetMinLogLevel(LOGGING_FATAL + 1); - EXPECT_FALSE(PA_LOG_IS_ON(INFO)); - EXPECT_FALSE(PA_LOG_IS_ON(WARNING)); - EXPECT_FALSE(PA_LOG_IS_ON(ERROR)); - // PA_LOG_IS_ON(FATAL) should always be true. - EXPECT_TRUE(PA_LOG_IS_ON(FATAL)); - // If BUILDFLAG(PA_DCHECK_IS_ON) then DFATAL is FATAL. - EXPECT_EQ(BUILDFLAG(PA_DCHECK_IS_ON), PA_LOG_IS_ON(DFATAL)); -} - -TEST(PALoggingTest, LoggingIsLazyBySeverity) { - MockLogSource mock_log_source; - EXPECT_CALL(mock_log_source, Log()).Times(0); - - SetMinLogLevel(LOGGING_WARNING); - - EXPECT_FALSE(PA_LOG_IS_ON(INFO)); - EXPECT_FALSE(PA_DLOG_IS_ON(INFO)); - EXPECT_FALSE(PA_VLOG_IS_ON(1)); - - PA_LOG(INFO) << mock_log_source.Log(); - PA_LOG_IF(INFO, false) << mock_log_source.Log(); - PA_PLOG(INFO) << mock_log_source.Log(); - PA_PLOG_IF(INFO, false) << mock_log_source.Log(); - PA_VLOG(1) << mock_log_source.Log(); - PA_VLOG_IF(1, true) << mock_log_source.Log(); - PA_VPLOG(1) << mock_log_source.Log(); - PA_VPLOG_IF(1, true) << mock_log_source.Log(); - - PA_DLOG(INFO) << mock_log_source.Log(); - PA_DLOG_IF(INFO, true) << mock_log_source.Log(); - PA_DPLOG(INFO) << mock_log_source.Log(); - PA_DPLOG_IF(INFO, true) << mock_log_source.Log(); - PA_DVLOG(1) << mock_log_source.Log(); - PA_DVLOG_IF(1, true) << mock_log_source.Log(); - PA_DVPLOG(1) << mock_log_source.Log(); - PA_DVPLOG_IF(1, true) << mock_log_source.Log(); -} - -// Always log-to-stderr(RawLog) if message handler is not assigned. -TEST(PALoggingTest, LogIsAlwaysToStdErr) { - MockLogSource mock_log_source_stderr; - SetMinLogLevel(LOGGING_INFO); - EXPECT_TRUE(PA_LOG_IS_ON(INFO)); - EXPECT_CALL(mock_log_source_stderr, Log()).Times(1).WillOnce(Return("foo")); - PA_LOG(INFO) << mock_log_source_stderr.Log(); -} - -TEST(PALoggingTest, DebugLoggingReleaseBehavior) { -#if BUILDFLAG(PA_DCHECK_IS_ON) - int debug_only_variable = 1; -#endif - // These should avoid emitting references to |debug_only_variable| - // in release mode. - PA_DLOG_IF(INFO, debug_only_variable) << "test"; - PA_DLOG_ASSERT(debug_only_variable) << "test"; - PA_DPLOG_IF(INFO, debug_only_variable) << "test"; - PA_DVLOG_IF(1, debug_only_variable) << "test"; -} - -} // namespace - -} // namespace partition_alloc::internal::logging
diff --git a/base/allocator/partition_allocator/partition_alloc_base/mac/foundation_util.h b/base/allocator/partition_allocator/partition_alloc_base/mac/foundation_util.h deleted file mode 100644 index 6960a3f..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/mac/foundation_util.h +++ /dev/null
@@ -1,58 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MAC_FOUNDATION_UTIL_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MAC_FOUNDATION_UTIL_H_ - -#include <CoreFoundation/CoreFoundation.h> - -namespace partition_alloc::internal::base::mac { - -// CFCast<>() and CFCastStrict<>() cast a basic CFTypeRef to a more -// specific CoreFoundation type. The compatibility of the passed -// object is found by comparing its opaque type against the -// requested type identifier. If the supplied object is not -// compatible with the requested return type, CFCast<>() returns -// NULL and CFCastStrict<>() will DCHECK. Providing a NULL pointer -// to either variant results in NULL being returned without -// triggering any DCHECK. -// -// Example usage: -// CFNumberRef some_number = base::mac::CFCast<CFNumberRef>( -// CFArrayGetValueAtIndex(array, index)); -// -// CFTypeRef hello = CFSTR("hello world"); -// CFStringRef some_string = base::mac::CFCastStrict<CFStringRef>(hello); - -template <typename T> -T CFCast(const CFTypeRef& cf_val); - -template <typename T> -T CFCastStrict(const CFTypeRef& cf_val); - -#define PA_CF_CAST_DECL(TypeCF) \ - template <> \ - TypeCF##Ref CFCast<TypeCF##Ref>(const CFTypeRef& cf_val); \ - \ - template <> \ - TypeCF##Ref CFCastStrict<TypeCF##Ref>(const CFTypeRef& cf_val) - -PA_CF_CAST_DECL(CFArray); -PA_CF_CAST_DECL(CFBag); -PA_CF_CAST_DECL(CFBoolean); -PA_CF_CAST_DECL(CFData); -PA_CF_CAST_DECL(CFDate); -PA_CF_CAST_DECL(CFDictionary); -PA_CF_CAST_DECL(CFNull); -PA_CF_CAST_DECL(CFNumber); -PA_CF_CAST_DECL(CFSet); -PA_CF_CAST_DECL(CFString); -PA_CF_CAST_DECL(CFURL); -PA_CF_CAST_DECL(CFUUID); - -#undef PA_CF_CAST_DECL - -} // namespace partition_alloc::internal::base::mac - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MAC_FOUNDATION_UTIL_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/mac/foundation_util.mm b/base/allocator/partition_allocator/partition_alloc_base/mac/foundation_util.mm deleted file mode 100644 index 77489ee..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/mac/foundation_util.mm +++ /dev/null
@@ -1,45 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/mac/foundation_util.h" - -#include "base/allocator/partition_allocator/partition_alloc_check.h" - -namespace partition_alloc::internal::base::mac { - -#define PA_CF_CAST_DEFN(TypeCF) \ - template <> \ - TypeCF##Ref CFCast<TypeCF##Ref>(const CFTypeRef& cf_val) { \ - if (cf_val == NULL) { \ - return NULL; \ - } \ - if (CFGetTypeID(cf_val) == TypeCF##GetTypeID()) { \ - return (TypeCF##Ref)(cf_val); \ - } \ - return NULL; \ - } \ - \ - template <> \ - TypeCF##Ref CFCastStrict<TypeCF##Ref>(const CFTypeRef& cf_val) { \ - TypeCF##Ref rv = CFCast<TypeCF##Ref>(cf_val); \ - PA_DCHECK(cf_val == NULL || rv); \ - return rv; \ - } - -PA_CF_CAST_DEFN(CFArray) -PA_CF_CAST_DEFN(CFBag) -PA_CF_CAST_DEFN(CFBoolean) -PA_CF_CAST_DEFN(CFData) -PA_CF_CAST_DEFN(CFDate) -PA_CF_CAST_DEFN(CFDictionary) -PA_CF_CAST_DEFN(CFNull) -PA_CF_CAST_DEFN(CFNumber) -PA_CF_CAST_DEFN(CFSet) -PA_CF_CAST_DEFN(CFString) -PA_CF_CAST_DEFN(CFURL) -PA_CF_CAST_DEFN(CFUUID) - -#undef PA_CF_CAST_DEFN - -} // namespace partition_alloc::internal::base::mac
diff --git a/base/allocator/partition_allocator/partition_alloc_base/mac/mac_util.h b/base/allocator/partition_allocator/partition_alloc_base/mac/mac_util.h deleted file mode 100644 index b9279f4..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/mac/mac_util.h +++ /dev/null
@@ -1,121 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MAC_MAC_UTIL_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MAC_MAC_UTIL_H_ - -#include <AvailabilityMacros.h> -#import <CoreGraphics/CoreGraphics.h> - -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" - -namespace partition_alloc::internal::base::mac { - -namespace internal { - -// Returns the system's macOS major and minor version numbers combined into an -// integer value. For example, for macOS Sierra this returns 1012, and for macOS -// Big Sur it returns 1100. Note that the accuracy returned by this function is -// as granular as the major version number of Darwin. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) int MacOSVersion(); - -} // namespace internal - -// Run-time OS version checks. Prefer @available in Objective-C files. If that -// is not possible, use these functions instead of -// base::SysInfo::OperatingSystemVersionNumbers. Prefer the "AtLeast" and -// "AtMost" variants to those that check for a specific version, unless you know -// for sure that you need to check for a specific version. - -#define PA_DEFINE_OLD_IS_OS_FUNCS_CR_MIN_REQUIRED(V, DEPLOYMENT_TARGET_TEST) \ - inline bool IsOS10_##V() { \ - DEPLOYMENT_TARGET_TEST(>, V, false) \ - return internal::MacOSVersion() == 1000 + V; \ - } \ - inline bool IsAtMostOS10_##V() { \ - DEPLOYMENT_TARGET_TEST(>, V, false) \ - return internal::MacOSVersion() <= 1000 + V; \ - } - -#define PA_DEFINE_OLD_IS_OS_FUNCS(V, DEPLOYMENT_TARGET_TEST) \ - PA_DEFINE_OLD_IS_OS_FUNCS_CR_MIN_REQUIRED(V, DEPLOYMENT_TARGET_TEST) \ - inline bool IsAtLeastOS10_##V() { \ - DEPLOYMENT_TARGET_TEST(>=, V, true) \ - return internal::MacOSVersion() >= 1000 + V; \ - } - -#define PA_DEFINE_IS_OS_FUNCS_CR_MIN_REQUIRED(V, DEPLOYMENT_TARGET_TEST) \ - inline bool IsOS##V() { \ - DEPLOYMENT_TARGET_TEST(>, V, false) \ - return internal::MacOSVersion() == V * 100; \ - } \ - inline bool IsAtMostOS##V() { \ - DEPLOYMENT_TARGET_TEST(>, V, false) \ - return internal::MacOSVersion() <= V * 100; \ - } - -#define PA_DEFINE_IS_OS_FUNCS(V, DEPLOYMENT_TARGET_TEST) \ - PA_DEFINE_IS_OS_FUNCS_CR_MIN_REQUIRED(V, DEPLOYMENT_TARGET_TEST) \ - inline bool IsAtLeastOS##V() { \ - DEPLOYMENT_TARGET_TEST(>=, V, true) \ - return internal::MacOSVersion() >= V * 100; \ - } - -#define PA_OLD_TEST_DEPLOYMENT_TARGET(OP, V, RET) \ - if (MAC_OS_X_VERSION_MIN_REQUIRED OP MAC_OS_X_VERSION_10_##V) \ - return RET; -#define PA_TEST_DEPLOYMENT_TARGET(OP, V, RET) \ - if (MAC_OS_X_VERSION_MIN_REQUIRED OP MAC_OS_VERSION_##V##_0) \ - return RET; -#define PA_IGNORE_DEPLOYMENT_TARGET(OP, V, RET) - -// Notes: -// - When bumping the minimum version of the macOS required by Chromium, remove -// lines from below corresponding to versions of the macOS no longer -// supported. Ensure that the minimum supported version uses the -// PA_DEFINE_OLD_IS_OS_FUNCS_CR_MIN_REQUIRED macro. When macOS 11.0 is the -// minimum required version, remove all the OLD versions of the macros. -// - When bumping the minimum version of the macOS SDK required to build -// Chromium, remove the #ifdef that switches between -// PA_TEST_DEPLOYMENT_TARGET and PA_IGNORE_DEPLOYMENT_TARGET. - -// Versions of macOS supported at runtime but whose SDK is not supported for -// building. -PA_DEFINE_OLD_IS_OS_FUNCS_CR_MIN_REQUIRED(13, PA_OLD_TEST_DEPLOYMENT_TARGET) -PA_DEFINE_OLD_IS_OS_FUNCS(14, PA_OLD_TEST_DEPLOYMENT_TARGET) -PA_DEFINE_OLD_IS_OS_FUNCS(15, PA_OLD_TEST_DEPLOYMENT_TARGET) -PA_DEFINE_IS_OS_FUNCS(11, PA_TEST_DEPLOYMENT_TARGET) - -// Versions of macOS supported at runtime and whose SDK is supported for -// building. -#ifdef MAC_OS_VERSION_12_0 -PA_DEFINE_IS_OS_FUNCS(12, PA_TEST_DEPLOYMENT_TARGET) -#else -PA_DEFINE_IS_OS_FUNCS(12, PA_IGNORE_DEPLOYMENT_TARGET) -#endif - -#ifdef MAC_OS_VERSION_13_0 -PA_DEFINE_IS_OS_FUNCS(13, PA_TEST_DEPLOYMENT_TARGET) -#else -PA_DEFINE_IS_OS_FUNCS(13, PA_IGNORE_DEPLOYMENT_TARGET) -#endif - -#undef PA_DEFINE_OLD_IS_OS_FUNCS_CR_MIN_REQUIRED -#undef PA_DEFINE_OLD_IS_OS_FUNCS -#undef PA_DEFINE_IS_OS_FUNCS_CR_MIN_REQUIRED -#undef PA_DEFINE_IS_OS_FUNCS -#undef PA_OLD_TEST_DEPLOYMENT_TARGET -#undef PA_TEST_DEPLOYMENT_TARGET -#undef PA_IGNORE_DEPLOYMENT_TARGET - -// This should be infrequently used. It only makes sense to use this to avoid -// codepaths that are very likely to break on future (unreleased, untested, -// unborn) OS releases, or to log when the OS is newer than any known version. -inline bool IsOSLaterThan13_DontCallThis() { - return !IsAtMostOS13(); -} - -} // namespace partition_alloc::internal::base::mac - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MAC_MAC_UTIL_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/mac/mac_util.mm b/base/allocator/partition_allocator/partition_alloc_base/mac/mac_util.mm deleted file mode 100644 index d766671..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/mac/mac_util.mm +++ /dev/null
@@ -1,104 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/mac/mac_util.h" - -#include <stddef.h> -#include <string.h> -#include <sys/sysctl.h> -#include <sys/types.h> -#include <sys/utsname.h> - -#include "base/allocator/partition_allocator/partition_alloc_base/logging.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" - -namespace partition_alloc::internal::base::mac { - -namespace { - -// Returns the running system's Darwin major version. Don't call this, it's an -// implementation detail and its result is meant to be cached by -// MacOSVersionInternal(). -int DarwinMajorVersionInternal() { - // base::OperatingSystemVersionNumbers() at one time called Gestalt(), which - // was observed to be able to spawn threads (see https://crbug.com/53200). - // Nowadays that function calls -[NSProcessInfo operatingSystemVersion], whose - // current implementation does things like hit the file system, which is - // possibly a blocking operation. Either way, it's overkill for what needs to - // be done here. - // - // uname, on the other hand, is implemented as a simple series of sysctl - // system calls to obtain the relevant data from the kernel. The data is - // compiled right into the kernel, so no threads or blocking or other - // funny business is necessary. - - struct utsname uname_info; - if (uname(&uname_info) != 0) { - PA_DPLOG(ERROR) << "uname"; - return 0; - } - - if (strcmp(uname_info.sysname, "Darwin") != 0) { - PA_DLOG(ERROR) << "unexpected uname sysname " << uname_info.sysname; - return 0; - } - - const char* dot = strchr(uname_info.release, '.'); - if (!dot || uname_info.release == dot || - // Darwin version should be 1 or 2 digits, it's unlikely to be more than - // 4 digits. - dot - uname_info.release > 4) { - PA_DLOG(ERROR) << "could not parse uname release " << uname_info.release; - return 0; - } - - int darwin_major_version = 0; - constexpr int base = 10; - for (const char* p = uname_info.release; p < dot; ++p) { - if (!('0' <= *p && *p < '0' + base)) { - PA_DLOG(ERROR) << "could not parse uname release " << uname_info.release; - return 0; - } - - // Since we checked the number of digits is 4 at most (see above), there is - // no chance to overflow. - darwin_major_version *= base; - darwin_major_version += *p - '0'; - } - - return darwin_major_version; -} - -// The implementation of MacOSVersion() as defined in the header. Don't call -// this, it's an implementation detail and the result is meant to be cached by -// MacOSVersion(). -int MacOSVersionInternal() { - int darwin_major_version = DarwinMajorVersionInternal(); - - // Darwin major versions 6 through 19 corresponded to macOS versions 10.2 - // through 10.15. - PA_CHECK(darwin_major_version >= 6); - if (darwin_major_version <= 19) - return 1000 + darwin_major_version - 4; - - // Darwin major version 20 corresponds to macOS version 11.0. Assume a - // correspondence between Darwin's major version numbers and macOS major - // version numbers. - int macos_major_version = darwin_major_version - 9; - - return macos_major_version * 100; -} - -} // namespace - -namespace internal { - -int MacOSVersion() { - static int macos_version = MacOSVersionInternal(); - return macos_version; -} - -} // namespace internal - -} // namespace partition_alloc::internal::base::mac
diff --git a/base/allocator/partition_allocator/partition_alloc_base/mac/scoped_cftyperef.h b/base/allocator/partition_allocator/partition_alloc_base/mac/scoped_cftyperef.h deleted file mode 100644 index 60919d0..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/mac/scoped_cftyperef.h +++ /dev/null
@@ -1,48 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MAC_SCOPED_CFTYPEREF_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MAC_SCOPED_CFTYPEREF_H_ - -#include <CoreFoundation/CoreFoundation.h> - -#include "base/allocator/partition_allocator/partition_alloc_base/mac/scoped_typeref.h" - -namespace partition_alloc::internal::base { - -// ScopedCFTypeRef<> is patterned after std::unique_ptr<>, but maintains -// ownership of a CoreFoundation object: any object that can be represented -// as a CFTypeRef. Style deviations here are solely for compatibility with -// std::unique_ptr<>'s interface, with which everyone is already familiar. -// -// By default, ScopedCFTypeRef<> takes ownership of an object (in the -// constructor or in reset()) by taking over the caller's existing ownership -// claim. The caller must own the object it gives to ScopedCFTypeRef<>, and -// relinquishes an ownership claim to that object. ScopedCFTypeRef<> does not -// call CFRetain(). This behavior is parameterized by the |OwnershipPolicy| -// enum. If the value |RETAIN| is passed (in the constructor or in reset()), -// then ScopedCFTypeRef<> will call CFRetain() on the object, and the initial -// ownership is not changed. - -namespace internal { - -template <typename CFT> -struct ScopedCFTypeRefTraits { - static CFT InvalidValue() { return nullptr; } - static CFT Retain(CFT object) { - CFRetain(object); - return object; - } - static void Release(CFT object) { CFRelease(object); } -}; - -} // namespace internal - -template <typename CFT> -using ScopedCFTypeRef = - ScopedTypeRef<CFT, internal::ScopedCFTypeRefTraits<CFT>>; - -} // namespace partition_alloc::internal::base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MAC_SCOPED_CFTYPEREF_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/mac/scoped_typeref.h b/base/allocator/partition_allocator/partition_alloc_base/mac/scoped_typeref.h deleted file mode 100644 index 55db806..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/mac/scoped_typeref.h +++ /dev/null
@@ -1,145 +0,0 @@ -// Copyright 2014 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MAC_SCOPED_TYPEREF_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MAC_SCOPED_TYPEREF_H_ - -#include "base/allocator/partition_allocator/partition_alloc_base/memory/scoped_policy.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" - -namespace partition_alloc::internal::base { - -// ScopedTypeRef<> is patterned after std::unique_ptr<>, but maintains ownership -// of a reference to any type that is maintained by Retain and Release methods. -// -// The Traits structure must provide the Retain and Release methods for type T. -// A default ScopedTypeRefTraits is used but not defined, and should be defined -// for each type to use this interface. For example, an appropriate definition -// of ScopedTypeRefTraits for CGLContextObj would be: -// -// template<> -// struct ScopedTypeRefTraits<CGLContextObj> { -// static CGLContextObj InvalidValue() { return nullptr; } -// static CGLContextObj Retain(CGLContextObj object) { -// CGLContextRetain(object); -// return object; -// } -// static void Release(CGLContextObj object) { CGLContextRelease(object); } -// }; -// -// For the many types that have pass-by-pointer create functions, the function -// InitializeInto() is provided to allow direct initialization and assumption -// of ownership of the object. For example, continuing to use the above -// CGLContextObj specialization: -// -// base::ScopedTypeRef<CGLContextObj> context; -// CGLCreateContext(pixel_format, share_group, context.InitializeInto()); -// -// For initialization with an existing object, the caller may specify whether -// the ScopedTypeRef<> being initialized is assuming the caller's existing -// ownership of the object (and should not call Retain in initialization) or if -// it should not assume this ownership and must create its own (by calling -// Retain in initialization). This behavior is based on the |policy| parameter, -// with |ASSUME| for the former and |RETAIN| for the latter. The default policy -// is to |ASSUME|. - -template <typename T> -struct ScopedTypeRefTraits; - -template <typename T, typename Traits = ScopedTypeRefTraits<T>> -class ScopedTypeRef { - public: - using element_type = T; - - explicit constexpr ScopedTypeRef( - element_type object = Traits::InvalidValue(), - base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME) - : object_(object) { - if (object_ && policy == base::scoped_policy::RETAIN) - object_ = Traits::Retain(object_); - } - - ScopedTypeRef(const ScopedTypeRef<T, Traits>& that) : object_(that.object_) { - if (object_) - object_ = Traits::Retain(object_); - } - - // This allows passing an object to a function that takes its superclass. - template <typename R, typename RTraits> - explicit ScopedTypeRef(const ScopedTypeRef<R, RTraits>& that_as_subclass) - : object_(that_as_subclass.get()) { - if (object_) - object_ = Traits::Retain(object_); - } - - ScopedTypeRef(ScopedTypeRef<T, Traits>&& that) : object_(that.object_) { - that.object_ = Traits::InvalidValue(); - } - - ~ScopedTypeRef() { - if (object_) - Traits::Release(object_); - } - - ScopedTypeRef& operator=(const ScopedTypeRef<T, Traits>& that) { - reset(that.get(), base::scoped_policy::RETAIN); - return *this; - } - - // This is to be used only to take ownership of objects that are created - // by pass-by-pointer create functions. To enforce this, require that the - // object be reset to NULL before this may be used. - [[nodiscard]] element_type* InitializeInto() { - PA_DCHECK(!object_); - return &object_; - } - - void reset(const ScopedTypeRef<T, Traits>& that) { - reset(that.get(), base::scoped_policy::RETAIN); - } - - void reset(element_type object = Traits::InvalidValue(), - base::scoped_policy::OwnershipPolicy policy = - base::scoped_policy::ASSUME) { - if (object && policy == base::scoped_policy::RETAIN) - object = Traits::Retain(object); - if (object_) - Traits::Release(object_); - object_ = object; - } - - bool operator==(const ScopedTypeRef& that) const { - return object_ == that.object_; - } - - bool operator!=(const ScopedTypeRef& that) const { - return object_ != that.object_; - } - - operator element_type() const { return object_; } - - element_type get() const { return object_; } - - void swap(ScopedTypeRef& that) { - element_type temp = that.object_; - that.object_ = object_; - object_ = temp; - } - - // ScopedTypeRef<>::release() is like std::unique_ptr<>::release. It is NOT - // a wrapper for Release(). To force a ScopedTypeRef<> object to call - // Release(), use ScopedTypeRef<>::reset(). - [[nodiscard]] element_type release() { - element_type temp = object_; - object_ = Traits::InvalidValue(); - return temp; - } - - private: - element_type object_; -}; - -} // namespace partition_alloc::internal::base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MAC_SCOPED_TYPEREF_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/memory/ref_counted.cc b/base/allocator/partition_allocator/partition_alloc_base/memory/ref_counted.cc deleted file mode 100644 index db7aede..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/memory/ref_counted.cc +++ /dev/null
@@ -1,48 +0,0 @@ -// Copyright 2011 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/memory/ref_counted.h" - -#include <limits> -#include <ostream> -#include <type_traits> - -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" - -namespace partition_alloc::internal::base::subtle { - -bool RefCountedThreadSafeBase::HasOneRef() const { - return ref_count_.IsOne(); -} - -bool RefCountedThreadSafeBase::HasAtLeastOneRef() const { - return !ref_count_.IsZero(); -} - -#if BUILDFLAG(PA_DCHECK_IS_ON) -RefCountedThreadSafeBase::~RefCountedThreadSafeBase() { - PA_DCHECK(in_dtor_) << "RefCountedThreadSafe object deleted without " - "calling Release()"; -} -#endif - -// For security and correctness, we check the arithmetic on ref counts. -// -// In an attempt to avoid binary bloat (from inlining the `CHECK`), we define -// these functions out-of-line. However, compilers are wily. Further testing may -// show that `PA_NOINLINE` helps or hurts. -// -#if !defined(ARCH_CPU_X86_FAMILY) -bool RefCountedThreadSafeBase::Release() const { - return ReleaseImpl(); -} -void RefCountedThreadSafeBase::AddRef() const { - AddRefImpl(); -} -void RefCountedThreadSafeBase::AddRefWithCheck() const { - AddRefWithCheckImpl(); -} -#endif - -} // namespace partition_alloc::internal::base::subtle
diff --git a/base/allocator/partition_allocator/partition_alloc_base/memory/ref_counted.h b/base/allocator/partition_allocator/partition_alloc_base/memory/ref_counted.h deleted file mode 100644 index afc269f..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/memory/ref_counted.h +++ /dev/null
@@ -1,187 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MEMORY_REF_COUNTED_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MEMORY_REF_COUNTED_H_ - -#include "base/allocator/partition_allocator/partition_alloc_base/atomic_ref_count.h" -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_base/memory/scoped_refptr.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "build/build_config.h" - -namespace partition_alloc::internal::base { -namespace subtle { - -class PA_COMPONENT_EXPORT(PARTITION_ALLOC) RefCountedThreadSafeBase { - public: - RefCountedThreadSafeBase(const RefCountedThreadSafeBase&) = delete; - RefCountedThreadSafeBase& operator=(const RefCountedThreadSafeBase&) = delete; - - bool HasOneRef() const; - bool HasAtLeastOneRef() const; - - protected: - explicit constexpr RefCountedThreadSafeBase(StartRefCountFromZeroTag) {} - explicit constexpr RefCountedThreadSafeBase(StartRefCountFromOneTag) - : ref_count_(1) { -#if BUILDFLAG(PA_DCHECK_IS_ON) - needs_adopt_ref_ = true; -#endif - } - -#if BUILDFLAG(PA_DCHECK_IS_ON) - ~RefCountedThreadSafeBase(); -#else - ~RefCountedThreadSafeBase() = default; -#endif - -// Release and AddRef are suitable for inlining on X86 because they generate -// very small code sequences. On other platforms (ARM), it causes a size -// regression and is probably not worth it. -#if defined(ARCH_CPU_X86_FAMILY) - // Returns true if the object should self-delete. - bool Release() const { return ReleaseImpl(); } - void AddRef() const { AddRefImpl(); } - void AddRefWithCheck() const { AddRefWithCheckImpl(); } -#else - // Returns true if the object should self-delete. - bool Release() const; - void AddRef() const; - void AddRefWithCheck() const; -#endif - - private: - template <typename U> - friend scoped_refptr<U> AdoptRef(U*); - - void Adopted() const { -#if BUILDFLAG(PA_DCHECK_IS_ON) - PA_DCHECK(needs_adopt_ref_); - needs_adopt_ref_ = false; -#endif - } - - PA_ALWAYS_INLINE void AddRefImpl() const { -#if BUILDFLAG(PA_DCHECK_IS_ON) - PA_DCHECK(!in_dtor_); - // This RefCounted object is created with non-zero reference count. - // The first reference to such a object has to be made by AdoptRef or - // MakeRefCounted. - PA_DCHECK(!needs_adopt_ref_); -#endif - ref_count_.Increment(); - } - - PA_ALWAYS_INLINE void AddRefWithCheckImpl() const { -#if BUILDFLAG(PA_DCHECK_IS_ON) - PA_DCHECK(!in_dtor_); - // This RefCounted object is created with non-zero reference count. - // The first reference to such a object has to be made by AdoptRef or - // MakeRefCounted. - PA_DCHECK(!needs_adopt_ref_); -#endif - PA_CHECK(ref_count_.Increment() > 0); - } - - PA_ALWAYS_INLINE bool ReleaseImpl() const { -#if BUILDFLAG(PA_DCHECK_IS_ON) - PA_DCHECK(!in_dtor_); - PA_DCHECK(!ref_count_.IsZero()); -#endif - if (!ref_count_.Decrement()) { -#if BUILDFLAG(PA_DCHECK_IS_ON) - in_dtor_ = true; -#endif - return true; - } - return false; - } - - mutable AtomicRefCount ref_count_{0}; -#if BUILDFLAG(PA_DCHECK_IS_ON) - mutable bool needs_adopt_ref_ = false; - mutable bool in_dtor_ = false; -#endif -}; - -} // namespace subtle - -// Forward declaration. -template <class T, typename Traits> -class RefCountedThreadSafe; - -// Default traits for RefCountedThreadSafe<T>. Deletes the object when its ref -// count reaches 0. Overload to delete it on a different thread etc. -template <typename T> -struct DefaultRefCountedThreadSafeTraits { - static void Destruct(const T* x) { - // Delete through RefCountedThreadSafe to make child classes only need to be - // friend with RefCountedThreadSafe instead of this struct, which is an - // implementation detail. - RefCountedThreadSafe<T, DefaultRefCountedThreadSafeTraits>::DeleteInternal( - x); - } -}; - -// -// A thread-safe variant of RefCounted<T> -// -// class MyFoo : public base::RefCountedThreadSafe<MyFoo> { -// ... -// }; -// -// If you're using the default trait, then you should add compile time -// asserts that no one else is deleting your object. i.e. -// private: -// friend class base::RefCountedThreadSafe<MyFoo>; -// ~MyFoo(); -// -// We can use REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE() with RefCountedThreadSafe -// too. See the comment above the RefCounted definition for details. -template <class T, typename Traits = DefaultRefCountedThreadSafeTraits<T>> -class RefCountedThreadSafe : public subtle::RefCountedThreadSafeBase { - public: - static constexpr subtle::StartRefCountFromZeroTag kRefCountPreference = - subtle::kStartRefCountFromZeroTag; - - explicit RefCountedThreadSafe() - : subtle::RefCountedThreadSafeBase(T::kRefCountPreference) {} - - RefCountedThreadSafe(const RefCountedThreadSafe&) = delete; - RefCountedThreadSafe& operator=(const RefCountedThreadSafe&) = delete; - - void AddRef() const { AddRefImpl(T::kRefCountPreference); } - - void Release() const { - if (subtle::RefCountedThreadSafeBase::Release()) { - PA_ANALYZER_SKIP_THIS_PATH(); - Traits::Destruct(static_cast<const T*>(this)); - } - } - - protected: - ~RefCountedThreadSafe() = default; - - private: - friend struct DefaultRefCountedThreadSafeTraits<T>; - template <typename U> - static void DeleteInternal(const U* x) { - delete x; - } - - void AddRefImpl(subtle::StartRefCountFromZeroTag) const { - subtle::RefCountedThreadSafeBase::AddRef(); - } - - void AddRefImpl(subtle::StartRefCountFromOneTag) const { - subtle::RefCountedThreadSafeBase::AddRefWithCheck(); - } -}; - -} // namespace partition_alloc::internal::base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MEMORY_REF_COUNTED_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/memory/scoped_policy.h b/base/allocator/partition_allocator/partition_alloc_base/memory/scoped_policy.h deleted file mode 100644 index dbbe675..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/memory/scoped_policy.h +++ /dev/null
@@ -1,23 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MEMORY_SCOPED_POLICY_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MEMORY_SCOPED_POLICY_H_ - -namespace partition_alloc::internal::base::scoped_policy { - -// Defines the ownership policy for a scoped object. -enum OwnershipPolicy { - // The scoped object takes ownership of an object by taking over an existing - // ownership claim. - ASSUME, - - // The scoped object will retain the object and any initial ownership is - // not changed. - RETAIN -}; - -} // namespace partition_alloc::internal::base::scoped_policy - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MEMORY_SCOPED_POLICY_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/memory/scoped_refptr.h b/base/allocator/partition_allocator/partition_alloc_base/memory/scoped_refptr.h deleted file mode 100644 index b20456c..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/memory/scoped_refptr.h +++ /dev/null
@@ -1,371 +0,0 @@ -// Copyright 2017 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MEMORY_SCOPED_REFPTR_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MEMORY_SCOPED_REFPTR_H_ - -#include <stddef.h> - -#include <iosfwd> -#include <type_traits> -#include <utility> - -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" - -namespace partition_alloc::internal { - -template <class T> -class scoped_refptr; - -namespace base { - -template <class, typename> -class RefCountedThreadSafe; - -template <typename T> -scoped_refptr<T> AdoptRef(T* t); - -namespace subtle { - -enum AdoptRefTag { kAdoptRefTag }; -enum StartRefCountFromZeroTag { kStartRefCountFromZeroTag }; -enum StartRefCountFromOneTag { kStartRefCountFromOneTag }; - -// scoped_refptr<T> is typically used with one of several RefCounted<T> base -// classes or with custom AddRef and Release methods. These overloads dispatch -// on which was used. - -template <typename T, typename U, typename V> -constexpr bool IsRefCountPreferenceOverridden( - const T*, - const RefCountedThreadSafe<U, V>*) { - return !std::is_same<std::decay_t<decltype(T::kRefCountPreference)>, - std::decay_t<decltype(U::kRefCountPreference)>>::value; -} - -constexpr bool IsRefCountPreferenceOverridden(...) { - return false; -} - -template <typename T, typename U, typename V> -constexpr void AssertRefCountBaseMatches(const T*, - const RefCountedThreadSafe<U, V>*) { - static_assert( - std::is_base_of_v<U, T>, - "T implements RefCountedThreadSafe<U>, but U is not a base of T."); -} - -constexpr void AssertRefCountBaseMatches(...) {} - -} // namespace subtle - -// Creates a scoped_refptr from a raw pointer without incrementing the reference -// count. Use this only for a newly created object whose reference count starts -// from 1 instead of 0. -template <typename T> -scoped_refptr<T> AdoptRef(T* obj) { - using Tag = std::decay_t<decltype(T::kRefCountPreference)>; - static_assert(std::is_same<subtle::StartRefCountFromOneTag, Tag>::value, - "Use AdoptRef only if the reference count starts from one."); - - PA_DCHECK(obj); - PA_DCHECK(obj->HasOneRef()); - obj->Adopted(); - return scoped_refptr<T>(obj, subtle::kAdoptRefTag); -} - -namespace subtle { - -template <typename T> -scoped_refptr<T> AdoptRefIfNeeded(T* obj, StartRefCountFromZeroTag) { - return scoped_refptr<T>(obj); -} - -template <typename T> -scoped_refptr<T> AdoptRefIfNeeded(T* obj, StartRefCountFromOneTag) { - return AdoptRef(obj); -} - -} // namespace subtle - -// Constructs an instance of T, which is a ref counted type, and wraps the -// object into a scoped_refptr<T>. -template <typename T, typename... Args> -scoped_refptr<T> MakeRefCounted(Args&&... args) { - T* obj = new T(std::forward<Args>(args)...); - return subtle::AdoptRefIfNeeded(obj, T::kRefCountPreference); -} - -// Takes an instance of T, which is a ref counted type, and wraps the object -// into a scoped_refptr<T>. -template <typename T> -scoped_refptr<T> WrapRefCounted(T* t) { - return scoped_refptr<T>(t); -} - -} // namespace base - -// -// A smart pointer class for reference counted objects. Use this class instead -// of calling AddRef and Release manually on a reference counted object to -// avoid common memory leaks caused by forgetting to Release an object -// reference. Sample usage: -// -// class MyFoo : public RefCounted<MyFoo> { -// ... -// private: -// friend class RefCounted<MyFoo>; // Allow destruction by RefCounted<>. -// ~MyFoo(); // Destructor must be private/protected. -// }; -// -// void some_function() { -// scoped_refptr<MyFoo> foo = MakeRefCounted<MyFoo>(); -// foo->Method(param); -// // |foo| is released when this function returns -// } -// -// void some_other_function() { -// scoped_refptr<MyFoo> foo = MakeRefCounted<MyFoo>(); -// ... -// foo.reset(); // explicitly releases |foo| -// ... -// if (foo) -// foo->Method(param); -// } -// -// The above examples show how scoped_refptr<T> acts like a pointer to T. -// Given two scoped_refptr<T> classes, it is also possible to exchange -// references between the two objects, like so: -// -// { -// scoped_refptr<MyFoo> a = MakeRefCounted<MyFoo>(); -// scoped_refptr<MyFoo> b; -// -// b.swap(a); -// // now, |b| references the MyFoo object, and |a| references nullptr. -// } -// -// To make both |a| and |b| in the above example reference the same MyFoo -// object, simply use the assignment operator: -// -// { -// scoped_refptr<MyFoo> a = MakeRefCounted<MyFoo>(); -// scoped_refptr<MyFoo> b; -// -// b = a; -// // now, |a| and |b| each own a reference to the same MyFoo object. -// } -// -// Also see Chromium's ownership and calling conventions: -// https://chromium.googlesource.com/chromium/src/+/lkgr/styleguide/c++/c++.md#object-ownership-and-calling-conventions -// Specifically: -// If the function (at least sometimes) takes a ref on a refcounted object, -// declare the param as scoped_refptr<T>. The caller can decide whether it -// wishes to transfer ownership (by calling std::move(t) when passing t) or -// retain its ref (by simply passing t directly). -// In other words, use scoped_refptr like you would a std::unique_ptr except -// in the odd case where it's required to hold on to a ref while handing one -// to another component (if a component merely needs to use t on the stack -// without keeping a ref: pass t as a raw T*). -template <class T> -class PA_TRIVIAL_ABI scoped_refptr { - public: - typedef T element_type; - - constexpr scoped_refptr() = default; - - // Allow implicit construction from nullptr. - constexpr scoped_refptr(std::nullptr_t) {} - - // Constructs from a raw pointer. Note that this constructor allows implicit - // conversion from T* to scoped_refptr<T> which is strongly discouraged. If - // you are creating a new ref-counted object please use - // base::MakeRefCounted<T>() or base::WrapRefCounted<T>(). Otherwise you - // should move or copy construct from an existing scoped_refptr<T> to the - // ref-counted object. - scoped_refptr(T* p) : ptr_(p) { - if (ptr_) - AddRef(ptr_); - } - - // Copy constructor. This is required in addition to the copy conversion - // constructor below. - scoped_refptr(const scoped_refptr& r) : scoped_refptr(r.ptr_) {} - - // Copy conversion constructor. - template <typename U, - typename = typename std::enable_if< - std::is_convertible<U*, T*>::value>::type> - scoped_refptr(const scoped_refptr<U>& r) : scoped_refptr(r.ptr_) {} - - // Move constructor. This is required in addition to the move conversion - // constructor below. - scoped_refptr(scoped_refptr&& r) noexcept : ptr_(r.ptr_) { r.ptr_ = nullptr; } - - // Move conversion constructor. - template <typename U, - typename = typename std::enable_if< - std::is_convertible<U*, T*>::value>::type> - scoped_refptr(scoped_refptr<U>&& r) noexcept : ptr_(r.ptr_) { - r.ptr_ = nullptr; - } - - ~scoped_refptr() { - static_assert(!base::subtle::IsRefCountPreferenceOverridden( - static_cast<T*>(nullptr), static_cast<T*>(nullptr)), - "It's unsafe to override the ref count preference." - " Please remove REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE" - " from subclasses."); - if (ptr_) - Release(ptr_); - } - - T* get() const { return ptr_; } - - T& operator*() const { - PA_DCHECK(ptr_); - return *ptr_; - } - - T* operator->() const { - PA_DCHECK(ptr_); - return ptr_; - } - - scoped_refptr& operator=(std::nullptr_t) { - reset(); - return *this; - } - - scoped_refptr& operator=(T* p) { return *this = scoped_refptr(p); } - - // Unified assignment operator. - scoped_refptr& operator=(scoped_refptr r) noexcept { - swap(r); - return *this; - } - - // Sets managed object to null and releases reference to the previous managed - // object, if it existed. - void reset() { scoped_refptr().swap(*this); } - - // Returns the owned pointer (if any), releasing ownership to the caller. The - // caller is responsible for managing the lifetime of the reference. - [[nodiscard]] T* release(); - - void swap(scoped_refptr& r) noexcept { std::swap(ptr_, r.ptr_); } - - explicit operator bool() const { return ptr_ != nullptr; } - - template <typename U> - bool operator==(const scoped_refptr<U>& rhs) const { - return ptr_ == rhs.get(); - } - - template <typename U> - bool operator!=(const scoped_refptr<U>& rhs) const { - return !operator==(rhs); - } - - template <typename U> - bool operator<(const scoped_refptr<U>& rhs) const { - return ptr_ < rhs.get(); - } - - protected: - T* ptr_ = nullptr; - - private: - template <typename U> - friend scoped_refptr<U> base::AdoptRef(U*); - - scoped_refptr(T* p, base::subtle::AdoptRefTag) : ptr_(p) {} - - // Friend required for move constructors that set r.ptr_ to null. - template <typename U> - friend class scoped_refptr; - - // Non-inline helpers to allow: - // class Opaque; - // extern template class scoped_refptr<Opaque>; - // Otherwise the compiler will complain that Opaque is an incomplete type. - static void AddRef(T* ptr); - static void Release(T* ptr); -}; - -template <typename T> -T* scoped_refptr<T>::release() { - T* ptr = ptr_; - ptr_ = nullptr; - return ptr; -} - -// static -template <typename T> -void scoped_refptr<T>::AddRef(T* ptr) { - base::subtle::AssertRefCountBaseMatches(ptr, ptr); - ptr->AddRef(); -} - -// static -template <typename T> -void scoped_refptr<T>::Release(T* ptr) { - base::subtle::AssertRefCountBaseMatches(ptr, ptr); - ptr->Release(); -} - -template <typename T, typename U> -bool operator==(const scoped_refptr<T>& lhs, const U* rhs) { - return lhs.get() == rhs; -} - -template <typename T, typename U> -bool operator==(const T* lhs, const scoped_refptr<U>& rhs) { - return lhs == rhs.get(); -} - -template <typename T> -bool operator==(const scoped_refptr<T>& lhs, std::nullptr_t null) { - return !static_cast<bool>(lhs); -} - -template <typename T> -bool operator==(std::nullptr_t null, const scoped_refptr<T>& rhs) { - return !static_cast<bool>(rhs); -} - -template <typename T, typename U> -bool operator!=(const scoped_refptr<T>& lhs, const U* rhs) { - return !operator==(lhs, rhs); -} - -template <typename T, typename U> -bool operator!=(const T* lhs, const scoped_refptr<U>& rhs) { - return !operator==(lhs, rhs); -} - -template <typename T> -bool operator!=(const scoped_refptr<T>& lhs, std::nullptr_t null) { - return !operator==(lhs, null); -} - -template <typename T> -bool operator!=(std::nullptr_t null, const scoped_refptr<T>& rhs) { - return !operator==(null, rhs); -} - -template <typename T> -std::ostream& operator<<(std::ostream& out, const scoped_refptr<T>& p) { - return out << p.get(); -} - -template <typename T> -void swap(scoped_refptr<T>& lhs, scoped_refptr<T>& rhs) noexcept { - lhs.swap(rhs); -} - -} // namespace partition_alloc::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MEMORY_SCOPED_REFPTR_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/native_library.cc b/base/allocator/partition_allocator/partition_alloc_base/native_library.cc deleted file mode 100644 index 6d3897f..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/native_library.cc +++ /dev/null
@@ -1,15 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/native_library.h" - -namespace partition_alloc::internal::base { - -NativeLibrary LoadNativeLibrary(const FilePath& library_path, - NativeLibraryLoadError* error) { - return LoadNativeLibraryWithOptions(library_path, NativeLibraryOptions(), - error); -} - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/native_library.h b/base/allocator/partition_allocator/partition_alloc_base/native_library.h deleted file mode 100644 index 219d1c0..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/native_library.h +++ /dev/null
@@ -1,97 +0,0 @@ -// Copyright 2011 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NATIVE_LIBRARY_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NATIVE_LIBRARY_H_ - -// This file defines a cross-platform "NativeLibrary" type which represents -// a loadable module. - -#include <string> - -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_base/files/file_path.h" -#include "build/build_config.h" - -#if BUILDFLAG(IS_WIN) -#include <windows.h> -#elif BUILDFLAG(IS_APPLE) -#import <CoreFoundation/CoreFoundation.h> -#endif // OS_* - -namespace partition_alloc::internal::base { - -#if BUILDFLAG(IS_WIN) -using NativeLibrary = HMODULE; -#elif BUILDFLAG(IS_APPLE) -enum NativeLibraryType { BUNDLE, DYNAMIC_LIB }; -enum NativeLibraryObjCStatus { - OBJC_UNKNOWN, - OBJC_PRESENT, - OBJC_NOT_PRESENT, -}; -struct NativeLibraryStruct { - NativeLibraryType type; - CFBundleRefNum bundle_resource_ref; - NativeLibraryObjCStatus objc_status; - union { - CFBundleRef bundle; - void* dylib; - }; -}; -using NativeLibrary = NativeLibraryStruct*; -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) -using NativeLibrary = void*; -#endif // OS_* - -struct PA_COMPONENT_EXPORT(PARTITION_ALLOC) NativeLibraryLoadError { -#if BUILDFLAG(IS_WIN) - NativeLibraryLoadError() : code(0) {} -#endif // BUILDFLAG(IS_WIN) - - // Returns a string representation of the load error. - std::string ToString() const; - -#if BUILDFLAG(IS_WIN) - DWORD code; -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) - std::string message; -#endif // BUILDFLAG(IS_WIN) -}; - -struct PA_COMPONENT_EXPORT(PARTITION_ALLOC) NativeLibraryOptions { - NativeLibraryOptions() = default; - NativeLibraryOptions(const NativeLibraryOptions& options) = default; - - // If |true|, a loaded library is required to prefer local symbol resolution - // before considering global symbols. Note that this is already the default - // behavior on most systems. Setting this to |false| does not guarantee the - // inverse, i.e., it does not force a preference for global symbols over local - // ones. - bool prefer_own_symbols = false; -}; - -// Loads a native library from disk. Release it with UnloadNativeLibrary when -// you're done. Returns NULL on failure. -// If |error| is not NULL, it may be filled in on load error. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -NativeLibrary LoadNativeLibrary(const FilePath& library_path, - NativeLibraryLoadError* error); - -// Loads a native library from disk. Release it with UnloadNativeLibrary when -// you're done. Returns NULL on failure. -// If |error| is not NULL, it may be filled in on load error. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -NativeLibrary LoadNativeLibraryWithOptions(const FilePath& library_path, - const NativeLibraryOptions& options, - NativeLibraryLoadError* error); - -// Gets a function pointer from a native library. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -void* GetFunctionPointerFromNativeLibrary(NativeLibrary library, - const std::string& name); - -} // namespace partition_alloc::internal::base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NATIVE_LIBRARY_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/native_library_pa_unittest.cc b/base/allocator/partition_allocator/partition_alloc_base/native_library_pa_unittest.cc deleted file mode 100644 index a2ef975..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/native_library_pa_unittest.cc +++ /dev/null
@@ -1,26 +0,0 @@ -// Copyright 2015 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/native_library.h" - -#include "base/allocator/partition_allocator/partition_alloc_base/files/file_path.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace partition_alloc::internal::base { - -const FilePath::CharType kDummyLibraryPath[] = - PA_FILE_PATH_LITERAL("dummy_library"); - -TEST(PartitionAllocBaseNativeLibraryTest, LoadFailure) { - NativeLibraryLoadError error; - EXPECT_FALSE(LoadNativeLibrary(FilePath(kDummyLibraryPath), &error)); - EXPECT_FALSE(error.ToString().empty()); -} - -// |error| is optional and can be null. -TEST(PartitionAllocBaseNativeLibraryTest, LoadFailureWithNullError) { - EXPECT_FALSE(LoadNativeLibrary(FilePath(kDummyLibraryPath), nullptr)); -} - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/native_library_posix.cc b/base/allocator/partition_allocator/partition_alloc_base/native_library_posix.cc deleted file mode 100644 index 5e9f5cf..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/native_library_posix.cc +++ /dev/null
@@ -1,56 +0,0 @@ -// Copyright 2011 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/native_library.h" - -#include <dlfcn.h> - -#include "base/allocator/partition_allocator/partition_alloc_base/files/file_path.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "build/build_config.h" - -namespace partition_alloc::internal::base { - -std::string NativeLibraryLoadError::ToString() const { - return message; -} - -NativeLibrary LoadNativeLibraryWithOptions(const FilePath& library_path, - const NativeLibraryOptions& options, - NativeLibraryLoadError* error) { - // TODO(1151236): Temporarily disable this ScopedBlockingCall. After making - // partition_alloc ScopedBlockingCall() to see the same blocking_observer_ - // in base's ScopedBlockingCall(), we will copy ScopedBlockingCall code and - // will enable this. - - // dlopen() opens the file off disk. - // ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK); - - // We deliberately do not use RTLD_DEEPBIND by default. For the history why, - // please refer to the bug tracker. Some useful bug reports to read include: - // http://crbug.com/17943, http://crbug.com/17557, http://crbug.com/36892, - // and http://crbug.com/40794. - int flags = RTLD_LAZY; -#if BUILDFLAG(IS_ANDROID) || !defined(RTLD_DEEPBIND) - // Certain platforms don't define RTLD_DEEPBIND. Android dlopen() requires - // further investigation, as it might vary across versions. Crash here to - // warn developers that they're trying to rely on uncertain behavior. - PA_CHECK(!options.prefer_own_symbols); -#else - if (options.prefer_own_symbols) - flags |= RTLD_DEEPBIND; -#endif - void* dl = dlopen(library_path.value().c_str(), flags); - if (!dl && error) - error->message = dlerror(); - - return dl; -} - -void* GetFunctionPointerFromNativeLibrary(NativeLibrary library, - const std::string& name) { - return dlsym(library, name.c_str()); -} - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/no_destructor.h b/base/allocator/partition_allocator/partition_alloc_base/no_destructor.h deleted file mode 100644 index 98c2867..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/no_destructor.h +++ /dev/null
@@ -1,132 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NO_DESTRUCTOR_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NO_DESTRUCTOR_H_ - -#include <new> -#include <type_traits> -#include <utility> - -namespace partition_alloc::internal::base { - -// Helper type to create a function-local static variable of type `T` when `T` -// has a non-trivial destructor. Storing a `T` in a `base::NoDestructor<T>` will -// prevent `~T()` from running, even when the variable goes out of scope. -// -// Useful when a variable has static storage duration but its type has a -// non-trivial destructor. Chromium bans global constructors and destructors: -// using a function-local static variable prevents the former, while using -// `base::NoDestructor<T>` prevents the latter. -// -// ## Caveats -// -// - Must only be used as a function-local static variable. Declaring a global -// variable of type `base::NoDestructor<T>` will still generate a global -// constructor; declaring a local or member variable will lead to memory leaks -// or other surprising and undesirable behaviour. -// -// - If the data is rarely used, consider creating it on demand rather than -// caching it for the lifetime of the program. Though `base::NoDestructor<T>` -// does not heap allocate, the compiler still reserves space in bss for -// storing `T`, which costs memory at runtime. -// -// - If `T` is trivially destructible, do not use `base::NoDestructor<T>`: -// -// const uint64_t GetUnstableSessionSeed() { -// // No need to use `base::NoDestructor<T>` as `uint64_t` is trivially -// // destructible and does not require a global destructor. -// static const uint64_t kSessionSeed = base::RandUint64(); -// return kSessionSeed; -// } -// -// ## Example Usage -// -// const std::string& GetDefaultText() { -// // Required since `static const std::string` requires a global destructor. -// static const base::NoDestructor<std::string> s("Hello world!"); -// return *s; -// } -// -// More complex initialization using a lambda: -// -// const std::string& GetRandomNonce() { -// // `nonce` is initialized with random data the first time this function is -// // called, but its value is fixed thereafter. -// static const base::NoDestructor<std::string> nonce([] { -// std::string s(16); -// crypto::RandString(s.data(), s.size()); -// return s; -// }()); -// return *nonce; -// } -// -// ## Thread safety -// -// Initialisation of function-local static variables is thread-safe since C++11. -// The standard guarantees that: -// -// - function-local static variables will be initialised the first time -// execution passes through the declaration. -// -// - if another thread's execution concurrently passes through the declaration -// in the middle of initialisation, that thread will wait for the in-progress -// initialisation to complete. -template <typename T> -class NoDestructor { - public: - static_assert( - !std::is_trivially_destructible_v<T>, - "T is trivially destructible; please use a function-local static " - "of type T directly instead"); - - // Not constexpr; just write static constexpr T x = ...; if the value should - // be a constexpr. - template <typename... Args> - explicit NoDestructor(Args&&... args) { - new (storage_) T(std::forward<Args>(args)...); - } - - // Allows copy and move construction of the contained type, to allow - // construction from an initializer list, e.g. for std::vector. - explicit NoDestructor(const T& x) { new (storage_) T(x); } - explicit NoDestructor(T&& x) { new (storage_) T(std::move(x)); } - - NoDestructor(const NoDestructor&) = delete; - NoDestructor& operator=(const NoDestructor&) = delete; - - ~NoDestructor() = default; - - const T& operator*() const { return *get(); } - T& operator*() { return *get(); } - - const T* operator->() const { return get(); } - T* operator->() { return get(); } - - const T* get() const { return reinterpret_cast<const T*>(storage_); } - T* get() { return reinterpret_cast<T*>(storage_); } - - private: - alignas(T) char storage_[sizeof(T)]; - -#if defined(LEAK_SANITIZER) - // TODO(https://crbug.com/812277): This is a hack to work around the fact - // that LSan doesn't seem to treat NoDestructor as a root for reachability - // analysis. This means that code like this: - // static base::NoDestructor<std::vector<int>> v({1, 2, 3}); - // is considered a leak. Using the standard leak sanitizer annotations to - // suppress leaks doesn't work: std::vector is implicitly constructed before - // calling the base::NoDestructor constructor. - // - // Unfortunately, I haven't been able to demonstrate this issue in simpler - // reproductions: until that's resolved, hold an explicit pointer to the - // placement-new'd object in leak sanitizer mode to help LSan realize that - // objects allocated by the contained type are still reachable. - T* storage_ptr_ = reinterpret_cast<T*>(storage_); -#endif // defined(LEAK_SANITIZER) -}; - -} // namespace partition_alloc::internal::base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NO_DESTRUCTOR_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/numerics/checked_math.h b/base/allocator/partition_allocator/partition_alloc_base/numerics/checked_math.h deleted file mode 100644 index 9f32938..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/numerics/checked_math.h +++ /dev/null
@@ -1,375 +0,0 @@ -// Copyright 2017 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_CHECKED_MATH_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_CHECKED_MATH_H_ - -#include <stddef.h> - -#include <limits> -#include <type_traits> - -#include "base/allocator/partition_allocator/partition_alloc_base/numerics/checked_math_impl.h" - -namespace partition_alloc::internal::base { -namespace internal { - -template <typename T> -class CheckedNumeric { - static_assert(std::is_arithmetic<T>::value, - "CheckedNumeric<T>: T must be a numeric type."); - - public: - template <typename Src> - friend class CheckedNumeric; - - using type = T; - - constexpr CheckedNumeric() = default; - - // Copy constructor. - template <typename Src> - constexpr CheckedNumeric(const CheckedNumeric<Src>& rhs) - : state_(rhs.state_.value(), rhs.IsValid()) {} - - // This is not an explicit constructor because we implicitly upgrade regular - // numerics to CheckedNumerics to make them easier to use. - template <typename Src> - constexpr CheckedNumeric(Src value) // NOLINT(runtime/explicit) - : state_(value) { - static_assert(UnderlyingType<Src>::is_numeric, "Argument must be numeric."); - } - - // This is not an explicit constructor because we want a seamless conversion - // from StrictNumeric types. - template <typename Src> - constexpr CheckedNumeric( - StrictNumeric<Src> value) // NOLINT(runtime/explicit) - : state_(static_cast<Src>(value)) {} - - // IsValid() - The public API to test if a CheckedNumeric is currently valid. - // A range checked destination type can be supplied using the Dst template - // parameter. - template <typename Dst = T> - constexpr bool IsValid() const { - return state_.is_valid() && - IsValueInRangeForNumericType<Dst>(state_.value()); - } - - // AssignIfValid(Dst) - Assigns the underlying value if it is currently valid - // and is within the range supported by the destination type. Returns true if - // successful and false otherwise. - template <typename Dst> -#if defined(__clang__) || defined(__GNUC__) - __attribute__((warn_unused_result)) -#elif defined(_MSC_VER) - _Check_return_ -#endif - constexpr bool - AssignIfValid(Dst* result) const { - return PA_BASE_NUMERICS_LIKELY(IsValid<Dst>()) - ? ((*result = static_cast<Dst>(state_.value())), true) - : false; - } - - // ValueOrDie() - The primary accessor for the underlying value. If the - // current state is not valid it will CHECK and crash. - // A range checked destination type can be supplied using the Dst template - // parameter, which will trigger a CHECK if the value is not in bounds for - // the destination. - // The CHECK behavior can be overridden by supplying a handler as a - // template parameter, for test code, etc. However, the handler cannot access - // the underlying value, and it is not available through other means. - template <typename Dst = T, class CheckHandler = CheckOnFailure> - constexpr StrictNumeric<Dst> ValueOrDie() const { - return PA_BASE_NUMERICS_LIKELY(IsValid<Dst>()) - ? static_cast<Dst>(state_.value()) - : CheckHandler::template HandleFailure<Dst>(); - } - - // ValueOrDefault(T default_value) - A convenience method that returns the - // current value if the state is valid, and the supplied default_value for - // any other state. - // A range checked destination type can be supplied using the Dst template - // parameter. WARNING: This function may fail to compile or CHECK at runtime - // if the supplied default_value is not within range of the destination type. - template <typename Dst = T, typename Src> - constexpr StrictNumeric<Dst> ValueOrDefault(const Src default_value) const { - return PA_BASE_NUMERICS_LIKELY(IsValid<Dst>()) - ? static_cast<Dst>(state_.value()) - : checked_cast<Dst>(default_value); - } - - // Returns a checked numeric of the specified type, cast from the current - // CheckedNumeric. If the current state is invalid or the destination cannot - // represent the result then the returned CheckedNumeric will be invalid. - template <typename Dst> - constexpr CheckedNumeric<typename UnderlyingType<Dst>::type> Cast() const { - return *this; - } - - // This friend method is available solely for providing more detailed logging - // in the tests. Do not implement it in production code, because the - // underlying values may change at any time. - template <typename U> - friend U GetNumericValueForTest(const CheckedNumeric<U>& src); - - // Prototypes for the supported arithmetic operator overloads. - template <typename Src> - constexpr CheckedNumeric& operator+=(const Src rhs); - template <typename Src> - constexpr CheckedNumeric& operator-=(const Src rhs); - template <typename Src> - constexpr CheckedNumeric& operator*=(const Src rhs); - template <typename Src> - constexpr CheckedNumeric& operator/=(const Src rhs); - template <typename Src> - constexpr CheckedNumeric& operator%=(const Src rhs); - template <typename Src> - constexpr CheckedNumeric& operator<<=(const Src rhs); - template <typename Src> - constexpr CheckedNumeric& operator>>=(const Src rhs); - template <typename Src> - constexpr CheckedNumeric& operator&=(const Src rhs); - template <typename Src> - constexpr CheckedNumeric& operator|=(const Src rhs); - template <typename Src> - constexpr CheckedNumeric& operator^=(const Src rhs); - - constexpr CheckedNumeric operator-() const { - // Use an optimized code path for a known run-time variable. - if (!PA_IsConstantEvaluated() && std::is_signed<T>::value && - std::is_floating_point<T>::value) { - return FastRuntimeNegate(); - } - // The negation of two's complement int min is int min. - const bool is_valid = - IsValid() && - (!std::is_signed<T>::value || std::is_floating_point<T>::value || - NegateWrapper(state_.value()) != std::numeric_limits<T>::lowest()); - return CheckedNumeric<T>(NegateWrapper(state_.value()), is_valid); - } - - constexpr CheckedNumeric operator~() const { - return CheckedNumeric<decltype(InvertWrapper(T()))>( - InvertWrapper(state_.value()), IsValid()); - } - - constexpr CheckedNumeric Abs() const { - return !IsValueNegative(state_.value()) ? *this : -*this; - } - - template <typename U> - constexpr CheckedNumeric<typename MathWrapper<CheckedMaxOp, T, U>::type> Max( - const U rhs) const { - return CheckMax(*this, rhs); - } - - template <typename U> - constexpr CheckedNumeric<typename MathWrapper<CheckedMinOp, T, U>::type> Min( - const U rhs) const { - return CheckMin(*this, rhs); - } - - // This function is available only for integral types. It returns an unsigned - // integer of the same width as the source type, containing the absolute value - // of the source, and properly handling signed min. - constexpr CheckedNumeric<typename UnsignedOrFloatForSize<T>::type> - UnsignedAbs() const { - return CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>( - SafeUnsignedAbs(state_.value()), state_.is_valid()); - } - - constexpr CheckedNumeric& operator++() { - *this += 1; - return *this; - } - - constexpr CheckedNumeric operator++(int) { - CheckedNumeric value = *this; - *this += 1; - return value; - } - - constexpr CheckedNumeric& operator--() { - *this -= 1; - return *this; - } - - constexpr CheckedNumeric operator--(int) { - // TODO(pkasting): Consider std::exchange() once it's constexpr in C++20. - const CheckedNumeric value = *this; - *this -= 1; - return value; - } - - // These perform the actual math operations on the CheckedNumerics. - // Binary arithmetic operations. - template <template <typename, typename, typename> class M, - typename L, - typename R> - static constexpr CheckedNumeric MathOp(const L lhs, const R rhs) { - using Math = typename MathWrapper<M, L, R>::math; - T result = 0; - const bool is_valid = - Wrapper<L>::is_valid(lhs) && Wrapper<R>::is_valid(rhs) && - Math::Do(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs), &result); - return CheckedNumeric<T>(result, is_valid); - } - - // Assignment arithmetic operations. - template <template <typename, typename, typename> class M, typename R> - constexpr CheckedNumeric& MathOp(const R rhs) { - using Math = typename MathWrapper<M, T, R>::math; - T result = 0; // Using T as the destination saves a range check. - const bool is_valid = - state_.is_valid() && Wrapper<R>::is_valid(rhs) && - Math::Do(state_.value(), Wrapper<R>::value(rhs), &result); - *this = CheckedNumeric<T>(result, is_valid); - return *this; - } - - private: - CheckedNumericState<T> state_; - - CheckedNumeric FastRuntimeNegate() const { - T result; - const bool success = CheckedSubOp<T, T>::Do(T(0), state_.value(), &result); - return CheckedNumeric<T>(result, IsValid() && success); - } - - template <typename Src> - constexpr CheckedNumeric(Src value, bool is_valid) - : state_(value, is_valid) {} - - // These wrappers allow us to handle state the same way for both - // CheckedNumeric and POD arithmetic types. - template <typename Src> - struct Wrapper { - static constexpr bool is_valid(Src) { return true; } - static constexpr Src value(Src value) { return value; } - }; - - template <typename Src> - struct Wrapper<CheckedNumeric<Src>> { - static constexpr bool is_valid(const CheckedNumeric<Src> v) { - return v.IsValid(); - } - static constexpr Src value(const CheckedNumeric<Src> v) { - return v.state_.value(); - } - }; - - template <typename Src> - struct Wrapper<StrictNumeric<Src>> { - static constexpr bool is_valid(const StrictNumeric<Src>) { return true; } - static constexpr Src value(const StrictNumeric<Src> v) { - return static_cast<Src>(v); - } - }; -}; - -// Convenience functions to avoid the ugly template disambiguator syntax. -template <typename Dst, typename Src> -constexpr bool IsValidForType(const CheckedNumeric<Src> value) { - return value.template IsValid<Dst>(); -} - -template <typename Dst, typename Src> -constexpr StrictNumeric<Dst> ValueOrDieForType( - const CheckedNumeric<Src> value) { - return value.template ValueOrDie<Dst>(); -} - -template <typename Dst, typename Src, typename Default> -constexpr StrictNumeric<Dst> ValueOrDefaultForType( - const CheckedNumeric<Src> value, - const Default default_value) { - return value.template ValueOrDefault<Dst>(default_value); -} - -// Convenience wrapper to return a new CheckedNumeric from the provided -// arithmetic or CheckedNumericType. -template <typename T> -constexpr CheckedNumeric<typename UnderlyingType<T>::type> MakeCheckedNum( - const T value) { - return value; -} - -// These implement the variadic wrapper for the math operations. -template <template <typename, typename, typename> class M, - typename L, - typename R> -constexpr CheckedNumeric<typename MathWrapper<M, L, R>::type> CheckMathOp( - const L lhs, - const R rhs) { - using Math = typename MathWrapper<M, L, R>::math; - return CheckedNumeric<typename Math::result_type>::template MathOp<M>(lhs, - rhs); -} - -// General purpose wrapper template for arithmetic operations. -template <template <typename, typename, typename> class M, - typename L, - typename R, - typename... Args> -constexpr auto CheckMathOp(const L lhs, const R rhs, const Args... args) { - return CheckMathOp<M>(CheckMathOp<M>(lhs, rhs), args...); -} - -PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Add, +, +=) -PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Sub, -, -=) -PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Mul, *, *=) -PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Div, /, /=) -PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Mod, %, %=) -PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Lsh, <<, <<=) -PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Rsh, >>, >>=) -PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, And, &, &=) -PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Or, |, |=) -PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Xor, ^, ^=) -PA_BASE_NUMERIC_ARITHMETIC_VARIADIC(Checked, Check, Max) -PA_BASE_NUMERIC_ARITHMETIC_VARIADIC(Checked, Check, Min) - -// These are some extra StrictNumeric operators to support simple pointer -// arithmetic with our result types. Since wrapping on a pointer is always -// bad, we trigger the CHECK condition here. -template <typename L, typename R> -L* operator+(L* lhs, const StrictNumeric<R> rhs) { - const uintptr_t result = CheckAdd(reinterpret_cast<uintptr_t>(lhs), - CheckMul(sizeof(L), static_cast<R>(rhs))) - .template ValueOrDie<uintptr_t>(); - return reinterpret_cast<L*>(result); -} - -template <typename L, typename R> -L* operator-(L* lhs, const StrictNumeric<R> rhs) { - const uintptr_t result = CheckSub(reinterpret_cast<uintptr_t>(lhs), - CheckMul(sizeof(L), static_cast<R>(rhs))) - .template ValueOrDie<uintptr_t>(); - return reinterpret_cast<L*>(result); -} - -} // namespace internal - -using internal::CheckAdd; -using internal::CheckAnd; -using internal::CheckDiv; -using internal::CheckedNumeric; -using internal::CheckLsh; -using internal::CheckMax; -using internal::CheckMin; -using internal::CheckMod; -using internal::CheckMul; -using internal::CheckOr; -using internal::CheckRsh; -using internal::CheckSub; -using internal::CheckXor; -using internal::IsValidForType; -using internal::MakeCheckedNum; -using internal::ValueOrDefaultForType; -using internal::ValueOrDieForType; - -} // namespace partition_alloc::internal::base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_CHECKED_MATH_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/numerics/checked_math_impl.h b/base/allocator/partition_allocator/partition_alloc_base/numerics/checked_math_impl.h deleted file mode 100644 index 3b1e95a..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/numerics/checked_math_impl.h +++ /dev/null
@@ -1,593 +0,0 @@ -// Copyright 2017 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_CHECKED_MATH_IMPL_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_CHECKED_MATH_IMPL_H_ - -#include <stddef.h> -#include <stdint.h> - -#include <climits> -#include <cmath> -#include <cstdlib> -#include <limits> -#include <type_traits> - -#include "base/allocator/partition_allocator/partition_alloc_base/numerics/safe_conversions.h" -#include "base/allocator/partition_allocator/partition_alloc_base/numerics/safe_math_shared_impl.h" - -namespace partition_alloc::internal::base::internal { - -template <typename T> -constexpr bool CheckedAddImpl(T x, T y, T* result) { - static_assert(std::is_integral<T>::value, "Type must be integral"); - // Since the value of x+y is undefined if we have a signed type, we compute - // it using the unsigned type of the same size. - using UnsignedDst = typename std::make_unsigned<T>::type; - using SignedDst = typename std::make_signed<T>::type; - const UnsignedDst ux = static_cast<UnsignedDst>(x); - const UnsignedDst uy = static_cast<UnsignedDst>(y); - const UnsignedDst uresult = static_cast<UnsignedDst>(ux + uy); - // Addition is valid if the sign of (x + y) is equal to either that of x or - // that of y. - if (std::is_signed<T>::value - ? static_cast<SignedDst>((uresult ^ ux) & (uresult ^ uy)) < 0 - : uresult < uy) // Unsigned is either valid or underflow. - return false; - *result = static_cast<T>(uresult); - return true; -} - -template <typename T, typename U, class Enable = void> -struct CheckedAddOp {}; - -template <typename T, typename U> -struct CheckedAddOp<T, - U, - typename std::enable_if<std::is_integral<T>::value && - std::is_integral<U>::value>::type> { - using result_type = typename MaxExponentPromotion<T, U>::type; - template <typename V> - static constexpr bool Do(T x, U y, V* result) { - if constexpr (CheckedAddFastOp<T, U>::is_supported) - return CheckedAddFastOp<T, U>::Do(x, y, result); - - // Double the underlying type up to a full machine word. - using FastPromotion = typename FastIntegerArithmeticPromotion<T, U>::type; - using Promotion = - typename std::conditional<(IntegerBitsPlusSign<FastPromotion>::value > - IntegerBitsPlusSign<intptr_t>::value), - typename BigEnoughPromotion<T, U>::type, - FastPromotion>::type; - // Fail if either operand is out of range for the promoted type. - // TODO(jschuh): This could be made to work for a broader range of values. - if (PA_BASE_NUMERICS_UNLIKELY( - !IsValueInRangeForNumericType<Promotion>(x) || - !IsValueInRangeForNumericType<Promotion>(y))) { - return false; - } - - Promotion presult = {}; - bool is_valid = true; - if (IsIntegerArithmeticSafe<Promotion, T, U>::value) { - presult = static_cast<Promotion>(x) + static_cast<Promotion>(y); - } else { - is_valid = CheckedAddImpl(static_cast<Promotion>(x), - static_cast<Promotion>(y), &presult); - } - if (!is_valid || !IsValueInRangeForNumericType<V>(presult)) - return false; - *result = static_cast<V>(presult); - return true; - } -}; - -template <typename T> -constexpr bool CheckedSubImpl(T x, T y, T* result) { - static_assert(std::is_integral<T>::value, "Type must be integral"); - // Since the value of x+y is undefined if we have a signed type, we compute - // it using the unsigned type of the same size. - using UnsignedDst = typename std::make_unsigned<T>::type; - using SignedDst = typename std::make_signed<T>::type; - const UnsignedDst ux = static_cast<UnsignedDst>(x); - const UnsignedDst uy = static_cast<UnsignedDst>(y); - const UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy); - // Subtraction is valid if either x and y have same sign, or (x-y) and x have - // the same sign. - if (std::is_signed<T>::value - ? static_cast<SignedDst>((uresult ^ ux) & (ux ^ uy)) < 0 - : x < y) - return false; - *result = static_cast<T>(uresult); - return true; -} - -template <typename T, typename U, class Enable = void> -struct CheckedSubOp {}; - -template <typename T, typename U> -struct CheckedSubOp<T, - U, - typename std::enable_if<std::is_integral<T>::value && - std::is_integral<U>::value>::type> { - using result_type = typename MaxExponentPromotion<T, U>::type; - template <typename V> - static constexpr bool Do(T x, U y, V* result) { - if constexpr (CheckedSubFastOp<T, U>::is_supported) - return CheckedSubFastOp<T, U>::Do(x, y, result); - - // Double the underlying type up to a full machine word. - using FastPromotion = typename FastIntegerArithmeticPromotion<T, U>::type; - using Promotion = - typename std::conditional<(IntegerBitsPlusSign<FastPromotion>::value > - IntegerBitsPlusSign<intptr_t>::value), - typename BigEnoughPromotion<T, U>::type, - FastPromotion>::type; - // Fail if either operand is out of range for the promoted type. - // TODO(jschuh): This could be made to work for a broader range of values. - if (PA_BASE_NUMERICS_UNLIKELY( - !IsValueInRangeForNumericType<Promotion>(x) || - !IsValueInRangeForNumericType<Promotion>(y))) { - return false; - } - - Promotion presult = {}; - bool is_valid = true; - if (IsIntegerArithmeticSafe<Promotion, T, U>::value) { - presult = static_cast<Promotion>(x) - static_cast<Promotion>(y); - } else { - is_valid = CheckedSubImpl(static_cast<Promotion>(x), - static_cast<Promotion>(y), &presult); - } - if (!is_valid || !IsValueInRangeForNumericType<V>(presult)) - return false; - *result = static_cast<V>(presult); - return true; - } -}; - -template <typename T> -constexpr bool CheckedMulImpl(T x, T y, T* result) { - static_assert(std::is_integral<T>::value, "Type must be integral"); - // Since the value of x*y is potentially undefined if we have a signed type, - // we compute it using the unsigned type of the same size. - using UnsignedDst = typename std::make_unsigned<T>::type; - using SignedDst = typename std::make_signed<T>::type; - const UnsignedDst ux = SafeUnsignedAbs(x); - const UnsignedDst uy = SafeUnsignedAbs(y); - const UnsignedDst uresult = static_cast<UnsignedDst>(ux * uy); - const bool is_negative = - std::is_signed<T>::value && static_cast<SignedDst>(x ^ y) < 0; - // We have a fast out for unsigned identity or zero on the second operand. - // After that it's an unsigned overflow check on the absolute value, with - // a +1 bound for a negative result. - if (uy > UnsignedDst(!std::is_signed<T>::value || is_negative) && - ux > (std::numeric_limits<T>::max() + UnsignedDst(is_negative)) / uy) - return false; - *result = static_cast<T>(is_negative ? 0 - uresult : uresult); - return true; -} - -template <typename T, typename U, class Enable = void> -struct CheckedMulOp {}; - -template <typename T, typename U> -struct CheckedMulOp<T, - U, - typename std::enable_if<std::is_integral<T>::value && - std::is_integral<U>::value>::type> { - using result_type = typename MaxExponentPromotion<T, U>::type; - template <typename V> - static constexpr bool Do(T x, U y, V* result) { - if constexpr (CheckedMulFastOp<T, U>::is_supported) - return CheckedMulFastOp<T, U>::Do(x, y, result); - - using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type; - // Verify the destination type can hold the result (always true for 0). - if (PA_BASE_NUMERICS_UNLIKELY( - (!IsValueInRangeForNumericType<Promotion>(x) || - !IsValueInRangeForNumericType<Promotion>(y)) && - x && y)) { - return false; - } - - Promotion presult = {}; - bool is_valid = true; - if (CheckedMulFastOp<Promotion, Promotion>::is_supported) { - // The fast op may be available with the promoted type. - is_valid = CheckedMulFastOp<Promotion, Promotion>::Do( - static_cast<Promotion>(x), static_cast<Promotion>(y), &presult); - } else if (IsIntegerArithmeticSafe<Promotion, T, U>::value) { - presult = static_cast<Promotion>(x) * static_cast<Promotion>(y); - } else { - is_valid = CheckedMulImpl(static_cast<Promotion>(x), - static_cast<Promotion>(y), &presult); - } - if (!is_valid || !IsValueInRangeForNumericType<V>(presult)) - return false; - *result = static_cast<V>(presult); - return true; - } -}; - -// Division just requires a check for a zero denominator or an invalid negation -// on signed min/-1. -template <typename T, typename U, class Enable = void> -struct CheckedDivOp {}; - -template <typename T, typename U> -struct CheckedDivOp<T, - U, - typename std::enable_if<std::is_integral<T>::value && - std::is_integral<U>::value>::type> { - using result_type = typename MaxExponentPromotion<T, U>::type; - template <typename V> - static constexpr bool Do(T x, U y, V* result) { - if (PA_BASE_NUMERICS_UNLIKELY(!y)) - return false; - - // The overflow check can be compiled away if we don't have the exact - // combination of types needed to trigger this case. - using Promotion = typename BigEnoughPromotion<T, U>::type; - if (PA_BASE_NUMERICS_UNLIKELY( - (std::is_signed<T>::value && std::is_signed<U>::value && - IsTypeInRangeForNumericType<T, Promotion>::value && - static_cast<Promotion>(x) == - std::numeric_limits<Promotion>::lowest() && - y == static_cast<U>(-1)))) { - return false; - } - - // This branch always compiles away if the above branch wasn't removed. - if (PA_BASE_NUMERICS_UNLIKELY( - (!IsValueInRangeForNumericType<Promotion>(x) || - !IsValueInRangeForNumericType<Promotion>(y)) && - x)) { - return false; - } - - const Promotion presult = Promotion(x) / Promotion(y); - if (!IsValueInRangeForNumericType<V>(presult)) - return false; - *result = static_cast<V>(presult); - return true; - } -}; - -template <typename T, typename U, class Enable = void> -struct CheckedModOp {}; - -template <typename T, typename U> -struct CheckedModOp<T, - U, - typename std::enable_if<std::is_integral<T>::value && - std::is_integral<U>::value>::type> { - using result_type = typename MaxExponentPromotion<T, U>::type; - template <typename V> - static constexpr bool Do(T x, U y, V* result) { - if (PA_BASE_NUMERICS_UNLIKELY(!y)) - return false; - - using Promotion = typename BigEnoughPromotion<T, U>::type; - if (PA_BASE_NUMERICS_UNLIKELY( - (std::is_signed<T>::value && std::is_signed<U>::value && - IsTypeInRangeForNumericType<T, Promotion>::value && - static_cast<Promotion>(x) == - std::numeric_limits<Promotion>::lowest() && - y == static_cast<U>(-1)))) { - *result = 0; - return true; - } - - const Promotion presult = - static_cast<Promotion>(x) % static_cast<Promotion>(y); - if (!IsValueInRangeForNumericType<V>(presult)) - return false; - *result = static_cast<Promotion>(presult); - return true; - } -}; - -template <typename T, typename U, class Enable = void> -struct CheckedLshOp {}; - -// Left shift. Shifts less than 0 or greater than or equal to the number -// of bits in the promoted type are undefined. Shifts of negative values -// are undefined. Otherwise it is defined when the result fits. -template <typename T, typename U> -struct CheckedLshOp<T, - U, - typename std::enable_if<std::is_integral<T>::value && - std::is_integral<U>::value>::type> { - using result_type = T; - template <typename V> - static constexpr bool Do(T x, U shift, V* result) { - // Disallow negative numbers and verify the shift is in bounds. - if (PA_BASE_NUMERICS_LIKELY( - !IsValueNegative(x) && - as_unsigned(shift) < as_unsigned(std::numeric_limits<T>::digits))) { - // Shift as unsigned to avoid undefined behavior. - *result = static_cast<V>(as_unsigned(x) << shift); - // If the shift can be reversed, we know it was valid. - return *result >> shift == x; - } - - // Handle the legal corner-case of a full-width signed shift of zero. - if (!std::is_signed<T>::value || x || - as_unsigned(shift) != as_unsigned(std::numeric_limits<T>::digits)) - return false; - *result = 0; - return true; - } -}; - -template <typename T, typename U, class Enable = void> -struct CheckedRshOp {}; - -// Right shift. Shifts less than 0 or greater than or equal to the number -// of bits in the promoted type are undefined. Otherwise, it is always defined, -// but a right shift of a negative value is implementation-dependent. -template <typename T, typename U> -struct CheckedRshOp<T, - U, - typename std::enable_if<std::is_integral<T>::value && - std::is_integral<U>::value>::type> { - using result_type = T; - template <typename V> - static constexpr bool Do(T x, U shift, V* result) { - // Use sign conversion to push negative values out of range. - if (PA_BASE_NUMERICS_UNLIKELY(as_unsigned(shift) >= - IntegerBitsPlusSign<T>::value)) { - return false; - } - - const T tmp = x >> shift; - if (!IsValueInRangeForNumericType<V>(tmp)) - return false; - *result = static_cast<V>(tmp); - return true; - } -}; - -template <typename T, typename U, class Enable = void> -struct CheckedAndOp {}; - -// For simplicity we support only unsigned integer results. -template <typename T, typename U> -struct CheckedAndOp<T, - U, - typename std::enable_if<std::is_integral<T>::value && - std::is_integral<U>::value>::type> { - using result_type = typename std::make_unsigned< - typename MaxExponentPromotion<T, U>::type>::type; - template <typename V> - static constexpr bool Do(T x, U y, V* result) { - const result_type tmp = - static_cast<result_type>(x) & static_cast<result_type>(y); - if (!IsValueInRangeForNumericType<V>(tmp)) - return false; - *result = static_cast<V>(tmp); - return true; - } -}; - -template <typename T, typename U, class Enable = void> -struct CheckedOrOp {}; - -// For simplicity we support only unsigned integers. -template <typename T, typename U> -struct CheckedOrOp<T, - U, - typename std::enable_if<std::is_integral<T>::value && - std::is_integral<U>::value>::type> { - using result_type = typename std::make_unsigned< - typename MaxExponentPromotion<T, U>::type>::type; - template <typename V> - static constexpr bool Do(T x, U y, V* result) { - const result_type tmp = - static_cast<result_type>(x) | static_cast<result_type>(y); - if (!IsValueInRangeForNumericType<V>(tmp)) - return false; - *result = static_cast<V>(tmp); - return true; - } -}; - -template <typename T, typename U, class Enable = void> -struct CheckedXorOp {}; - -// For simplicity we support only unsigned integers. -template <typename T, typename U> -struct CheckedXorOp<T, - U, - typename std::enable_if<std::is_integral<T>::value && - std::is_integral<U>::value>::type> { - using result_type = typename std::make_unsigned< - typename MaxExponentPromotion<T, U>::type>::type; - template <typename V> - static constexpr bool Do(T x, U y, V* result) { - const result_type tmp = - static_cast<result_type>(x) ^ static_cast<result_type>(y); - if (!IsValueInRangeForNumericType<V>(tmp)) - return false; - *result = static_cast<V>(tmp); - return true; - } -}; - -// Max doesn't really need to be implemented this way because it can't fail, -// but it makes the code much cleaner to use the MathOp wrappers. -template <typename T, typename U, class Enable = void> -struct CheckedMaxOp {}; - -template <typename T, typename U> -struct CheckedMaxOp< - T, - U, - typename std::enable_if<std::is_arithmetic<T>::value && - std::is_arithmetic<U>::value>::type> { - using result_type = typename MaxExponentPromotion<T, U>::type; - template <typename V> - static constexpr bool Do(T x, U y, V* result) { - const result_type tmp = IsGreater<T, U>::Test(x, y) - ? static_cast<result_type>(x) - : static_cast<result_type>(y); - if (!IsValueInRangeForNumericType<V>(tmp)) - return false; - *result = static_cast<V>(tmp); - return true; - } -}; - -// Min doesn't really need to be implemented this way because it can't fail, -// but it makes the code much cleaner to use the MathOp wrappers. -template <typename T, typename U, class Enable = void> -struct CheckedMinOp {}; - -template <typename T, typename U> -struct CheckedMinOp< - T, - U, - typename std::enable_if<std::is_arithmetic<T>::value && - std::is_arithmetic<U>::value>::type> { - using result_type = typename LowestValuePromotion<T, U>::type; - template <typename V> - static constexpr bool Do(T x, U y, V* result) { - const result_type tmp = IsLess<T, U>::Test(x, y) - ? static_cast<result_type>(x) - : static_cast<result_type>(y); - if (!IsValueInRangeForNumericType<V>(tmp)) - return false; - *result = static_cast<V>(tmp); - return true; - } -}; - -// This is just boilerplate that wraps the standard floating point arithmetic. -// A macro isn't the nicest solution, but it beats rewriting these repeatedly. -#define PA_BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \ - template <typename T, typename U> \ - struct Checked##NAME##Op< \ - T, U, \ - typename std::enable_if<std::is_floating_point<T>::value || \ - std::is_floating_point<U>::value>::type> { \ - using result_type = typename MaxExponentPromotion<T, U>::type; \ - template <typename V> \ - static constexpr bool Do(T x, U y, V* result) { \ - using Promotion = typename MaxExponentPromotion<T, U>::type; \ - const Promotion presult = x OP y; \ - if (!IsValueInRangeForNumericType<V>(presult)) \ - return false; \ - *result = static_cast<V>(presult); \ - return true; \ - } \ - }; - -PA_BASE_FLOAT_ARITHMETIC_OPS(Add, +) -PA_BASE_FLOAT_ARITHMETIC_OPS(Sub, -) -PA_BASE_FLOAT_ARITHMETIC_OPS(Mul, *) -PA_BASE_FLOAT_ARITHMETIC_OPS(Div, /) - -#undef PA_BASE_FLOAT_ARITHMETIC_OPS - -// Floats carry around their validity state with them, but integers do not. So, -// we wrap the underlying value in a specialization in order to hide that detail -// and expose an interface via accessors. -enum NumericRepresentation { - NUMERIC_INTEGER, - NUMERIC_FLOATING, - NUMERIC_UNKNOWN -}; - -template <typename NumericType> -struct GetNumericRepresentation { - static const NumericRepresentation value = - std::is_integral<NumericType>::value - ? NUMERIC_INTEGER - : (std::is_floating_point<NumericType>::value ? NUMERIC_FLOATING - : NUMERIC_UNKNOWN); -}; - -template <typename T, - NumericRepresentation type = GetNumericRepresentation<T>::value> -class CheckedNumericState {}; - -// Integrals require quite a bit of additional housekeeping to manage state. -template <typename T> -class CheckedNumericState<T, NUMERIC_INTEGER> { - public: - template <typename Src = int> - constexpr explicit CheckedNumericState(Src value = 0, bool is_valid = true) - : is_valid_(is_valid && IsValueInRangeForNumericType<T>(value)), - value_(WellDefinedConversionOrZero(value, is_valid_)) { - static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric."); - } - - template <typename Src> - constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs) - : CheckedNumericState(rhs.value(), rhs.is_valid()) {} - - constexpr bool is_valid() const { return is_valid_; } - - constexpr T value() const { return value_; } - - private: - // Ensures that a type conversion does not trigger undefined behavior. - template <typename Src> - static constexpr T WellDefinedConversionOrZero(Src value, bool is_valid) { - using SrcType = typename internal::UnderlyingType<Src>::type; - return (std::is_integral<SrcType>::value || is_valid) - ? static_cast<T>(value) - : 0; - } - - // is_valid_ precedes value_ because member initializers in the constructors - // are evaluated in field order, and is_valid_ must be read when initializing - // value_. - bool is_valid_; - T value_; -}; - -// Floating points maintain their own validity, but need translation wrappers. -template <typename T> -class CheckedNumericState<T, NUMERIC_FLOATING> { - public: - template <typename Src = double> - constexpr explicit CheckedNumericState(Src value = 0.0, bool is_valid = true) - : value_(WellDefinedConversionOrNaN( - value, - is_valid && IsValueInRangeForNumericType<T>(value))) {} - - template <typename Src> - constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs) - : CheckedNumericState(rhs.value(), rhs.is_valid()) {} - - constexpr bool is_valid() const { - // Written this way because std::isfinite is not reliably constexpr. - return PA_IsConstantEvaluated() - ? value_ <= std::numeric_limits<T>::max() && - value_ >= std::numeric_limits<T>::lowest() - : std::isfinite(value_); - } - - constexpr T value() const { return value_; } - - private: - // Ensures that a type conversion does not trigger undefined behavior. - template <typename Src> - static constexpr T WellDefinedConversionOrNaN(Src value, bool is_valid) { - using SrcType = typename internal::UnderlyingType<Src>::type; - return (StaticDstRangeRelationToSrcRange<T, SrcType>::value == - NUMERIC_RANGE_CONTAINED || - is_valid) - ? static_cast<T>(value) - : std::numeric_limits<T>::quiet_NaN(); - } - - T value_; -}; - -} // namespace partition_alloc::internal::base::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_CHECKED_MATH_IMPL_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/numerics/clamped_math.h b/base/allocator/partition_allocator/partition_alloc_base/numerics/clamped_math.h deleted file mode 100644 index ae3ea39..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/numerics/clamped_math.h +++ /dev/null
@@ -1,254 +0,0 @@ -// Copyright 2017 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_CLAMPED_MATH_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_CLAMPED_MATH_H_ - -#include <stddef.h> - -#include <limits> -#include <type_traits> - -#include "base/allocator/partition_allocator/partition_alloc_base/numerics/clamped_math_impl.h" - -namespace partition_alloc::internal::base { -namespace internal { - -template <typename T> -class ClampedNumeric { - static_assert(std::is_arithmetic<T>::value, - "ClampedNumeric<T>: T must be a numeric type."); - - public: - using type = T; - - constexpr ClampedNumeric() : value_(0) {} - - // Copy constructor. - template <typename Src> - constexpr ClampedNumeric(const ClampedNumeric<Src>& rhs) - : value_(saturated_cast<T>(rhs.value_)) {} - - template <typename Src> - friend class ClampedNumeric; - - // This is not an explicit constructor because we implicitly upgrade regular - // numerics to ClampedNumerics to make them easier to use. - template <typename Src> - constexpr ClampedNumeric(Src value) // NOLINT(runtime/explicit) - : value_(saturated_cast<T>(value)) { - static_assert(UnderlyingType<Src>::is_numeric, "Argument must be numeric."); - } - - // This is not an explicit constructor because we want a seamless conversion - // from StrictNumeric types. - template <typename Src> - constexpr ClampedNumeric( - StrictNumeric<Src> value) // NOLINT(runtime/explicit) - : value_(saturated_cast<T>(static_cast<Src>(value))) {} - - // Returns a ClampedNumeric of the specified type, cast from the current - // ClampedNumeric, and saturated to the destination type. - template <typename Dst> - constexpr ClampedNumeric<typename UnderlyingType<Dst>::type> Cast() const { - return *this; - } - - // Prototypes for the supported arithmetic operator overloads. - template <typename Src> - constexpr ClampedNumeric& operator+=(const Src rhs); - template <typename Src> - constexpr ClampedNumeric& operator-=(const Src rhs); - template <typename Src> - constexpr ClampedNumeric& operator*=(const Src rhs); - template <typename Src> - constexpr ClampedNumeric& operator/=(const Src rhs); - template <typename Src> - constexpr ClampedNumeric& operator%=(const Src rhs); - template <typename Src> - constexpr ClampedNumeric& operator<<=(const Src rhs); - template <typename Src> - constexpr ClampedNumeric& operator>>=(const Src rhs); - template <typename Src> - constexpr ClampedNumeric& operator&=(const Src rhs); - template <typename Src> - constexpr ClampedNumeric& operator|=(const Src rhs); - template <typename Src> - constexpr ClampedNumeric& operator^=(const Src rhs); - - constexpr ClampedNumeric operator-() const { - // The negation of two's complement int min is int min, so that's the - // only overflow case where we will saturate. - return ClampedNumeric<T>(SaturatedNegWrapper(value_)); - } - - constexpr ClampedNumeric operator~() const { - return ClampedNumeric<decltype(InvertWrapper(T()))>(InvertWrapper(value_)); - } - - constexpr ClampedNumeric Abs() const { - // The negation of two's complement int min is int min, so that's the - // only overflow case where we will saturate. - return ClampedNumeric<T>(SaturatedAbsWrapper(value_)); - } - - template <typename U> - constexpr ClampedNumeric<typename MathWrapper<ClampedMaxOp, T, U>::type> Max( - const U rhs) const { - using result_type = typename MathWrapper<ClampedMaxOp, T, U>::type; - return ClampedNumeric<result_type>( - ClampedMaxOp<T, U>::Do(value_, Wrapper<U>::value(rhs))); - } - - template <typename U> - constexpr ClampedNumeric<typename MathWrapper<ClampedMinOp, T, U>::type> Min( - const U rhs) const { - using result_type = typename MathWrapper<ClampedMinOp, T, U>::type; - return ClampedNumeric<result_type>( - ClampedMinOp<T, U>::Do(value_, Wrapper<U>::value(rhs))); - } - - // This function is available only for integral types. It returns an unsigned - // integer of the same width as the source type, containing the absolute value - // of the source, and properly handling signed min. - constexpr ClampedNumeric<typename UnsignedOrFloatForSize<T>::type> - UnsignedAbs() const { - return ClampedNumeric<typename UnsignedOrFloatForSize<T>::type>( - SafeUnsignedAbs(value_)); - } - - constexpr ClampedNumeric& operator++() { - *this += 1; - return *this; - } - - constexpr ClampedNumeric operator++(int) { - ClampedNumeric value = *this; - *this += 1; - return value; - } - - constexpr ClampedNumeric& operator--() { - *this -= 1; - return *this; - } - - constexpr ClampedNumeric operator--(int) { - ClampedNumeric value = *this; - *this -= 1; - return value; - } - - // These perform the actual math operations on the ClampedNumerics. - // Binary arithmetic operations. - template <template <typename, typename, typename> class M, - typename L, - typename R> - static constexpr ClampedNumeric MathOp(const L lhs, const R rhs) { - using Math = typename MathWrapper<M, L, R>::math; - return ClampedNumeric<T>( - Math::template Do<T>(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs))); - } - - // Assignment arithmetic operations. - template <template <typename, typename, typename> class M, typename R> - constexpr ClampedNumeric& MathOp(const R rhs) { - using Math = typename MathWrapper<M, T, R>::math; - *this = - ClampedNumeric<T>(Math::template Do<T>(value_, Wrapper<R>::value(rhs))); - return *this; - } - - template <typename Dst> - constexpr operator Dst() const { - return saturated_cast<typename ArithmeticOrUnderlyingEnum<Dst>::type>( - value_); - } - - // This method extracts the raw integer value without saturating it to the - // destination type as the conversion operator does. This is useful when - // e.g. assigning to an auto type or passing as a deduced template parameter. - constexpr T RawValue() const { return value_; } - - private: - T value_; - - // These wrappers allow us to handle state the same way for both - // ClampedNumeric and POD arithmetic types. - template <typename Src> - struct Wrapper { - static constexpr typename UnderlyingType<Src>::type value(Src value) { - return value; - } - }; -}; - -// Convenience wrapper to return a new ClampedNumeric from the provided -// arithmetic or ClampedNumericType. -template <typename T> -constexpr ClampedNumeric<typename UnderlyingType<T>::type> MakeClampedNum( - const T value) { - return value; -} - -// These implement the variadic wrapper for the math operations. -template <template <typename, typename, typename> class M, - typename L, - typename R> -constexpr ClampedNumeric<typename MathWrapper<M, L, R>::type> ClampMathOp( - const L lhs, - const R rhs) { - using Math = typename MathWrapper<M, L, R>::math; - return ClampedNumeric<typename Math::result_type>::template MathOp<M>(lhs, - rhs); -} - -// General purpose wrapper template for arithmetic operations. -template <template <typename, typename, typename> class M, - typename L, - typename R, - typename... Args> -constexpr auto ClampMathOp(const L lhs, const R rhs, const Args... args) { - return ClampMathOp<M>(ClampMathOp<M>(lhs, rhs), args...); -} - -PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Add, +, +=) -PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Sub, -, -=) -PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Mul, *, *=) -PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Div, /, /=) -PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Mod, %, %=) -PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Lsh, <<, <<=) -PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Rsh, >>, >>=) -PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, And, &, &=) -PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Or, |, |=) -PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Xor, ^, ^=) -PA_BASE_NUMERIC_ARITHMETIC_VARIADIC(Clamped, Clamp, Max) -PA_BASE_NUMERIC_ARITHMETIC_VARIADIC(Clamped, Clamp, Min) -PA_BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsLess, <) -PA_BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsLessOrEqual, <=) -PA_BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsGreater, >) -PA_BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsGreaterOrEqual, >=) -PA_BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsEqual, ==) -PA_BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsNotEqual, !=) - -} // namespace internal - -using internal::ClampAdd; -using internal::ClampAnd; -using internal::ClampDiv; -using internal::ClampedNumeric; -using internal::ClampLsh; -using internal::ClampMax; -using internal::ClampMin; -using internal::ClampMod; -using internal::ClampMul; -using internal::ClampOr; -using internal::ClampRsh; -using internal::ClampSub; -using internal::ClampXor; -using internal::MakeClampedNum; - -} // namespace partition_alloc::internal::base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_CLAMPED_MATH_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/numerics/clamped_math_impl.h b/base/allocator/partition_allocator/partition_alloc_base/numerics/clamped_math_impl.h deleted file mode 100644 index ab026cf..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/numerics/clamped_math_impl.h +++ /dev/null
@@ -1,338 +0,0 @@ -// Copyright 2017 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_CLAMPED_MATH_IMPL_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_CLAMPED_MATH_IMPL_H_ - -#include <stddef.h> -#include <stdint.h> - -#include <climits> -#include <cmath> -#include <cstdlib> -#include <limits> -#include <type_traits> - -#include "base/allocator/partition_allocator/partition_alloc_base/numerics/checked_math.h" -#include "base/allocator/partition_allocator/partition_alloc_base/numerics/safe_conversions.h" -#include "base/allocator/partition_allocator/partition_alloc_base/numerics/safe_math_shared_impl.h" - -namespace partition_alloc::internal::base::internal { - -template <typename T, - typename std::enable_if<std::is_integral<T>::value && - std::is_signed<T>::value>::type* = nullptr> -constexpr T SaturatedNegWrapper(T value) { - return PA_IsConstantEvaluated() || !ClampedNegFastOp<T>::is_supported - ? (NegateWrapper(value) != std::numeric_limits<T>::lowest() - ? NegateWrapper(value) - : std::numeric_limits<T>::max()) - : ClampedNegFastOp<T>::Do(value); -} - -template <typename T, - typename std::enable_if<std::is_integral<T>::value && - !std::is_signed<T>::value>::type* = nullptr> -constexpr T SaturatedNegWrapper(T value) { - return T(0); -} - -template < - typename T, - typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr> -constexpr T SaturatedNegWrapper(T value) { - return -value; -} - -template <typename T, - typename std::enable_if<std::is_integral<T>::value>::type* = nullptr> -constexpr T SaturatedAbsWrapper(T value) { - // The calculation below is a static identity for unsigned types, but for - // signed integer types it provides a non-branching, saturated absolute value. - // This works because SafeUnsignedAbs() returns an unsigned type, which can - // represent the absolute value of all negative numbers of an equal-width - // integer type. The call to IsValueNegative() then detects overflow in the - // special case of numeric_limits<T>::min(), by evaluating the bit pattern as - // a signed integer value. If it is the overflow case, we end up subtracting - // one from the unsigned result, thus saturating to numeric_limits<T>::max(). - return static_cast<T>( - SafeUnsignedAbs(value) - - IsValueNegative<T>(static_cast<T>(SafeUnsignedAbs(value)))); -} - -template < - typename T, - typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr> -constexpr T SaturatedAbsWrapper(T value) { - return value < 0 ? -value : value; -} - -template <typename T, typename U, class Enable = void> -struct ClampedAddOp {}; - -template <typename T, typename U> -struct ClampedAddOp<T, - U, - typename std::enable_if<std::is_integral<T>::value && - std::is_integral<U>::value>::type> { - using result_type = typename MaxExponentPromotion<T, U>::type; - template <typename V = result_type> - static constexpr V Do(T x, U y) { - if (!PA_IsConstantEvaluated() && ClampedAddFastOp<T, U>::is_supported) - return ClampedAddFastOp<T, U>::template Do<V>(x, y); - - static_assert(std::is_same<V, result_type>::value || - IsTypeInRangeForNumericType<U, V>::value, - "The saturation result cannot be determined from the " - "provided types."); - const V saturated = CommonMaxOrMin<V>(IsValueNegative(y)); - V result = {}; - return PA_BASE_NUMERICS_LIKELY((CheckedAddOp<T, U>::Do(x, y, &result))) - ? result - : saturated; - } -}; - -template <typename T, typename U, class Enable = void> -struct ClampedSubOp {}; - -template <typename T, typename U> -struct ClampedSubOp<T, - U, - typename std::enable_if<std::is_integral<T>::value && - std::is_integral<U>::value>::type> { - using result_type = typename MaxExponentPromotion<T, U>::type; - template <typename V = result_type> - static constexpr V Do(T x, U y) { - if (!PA_IsConstantEvaluated() && ClampedSubFastOp<T, U>::is_supported) - return ClampedSubFastOp<T, U>::template Do<V>(x, y); - - static_assert(std::is_same<V, result_type>::value || - IsTypeInRangeForNumericType<U, V>::value, - "The saturation result cannot be determined from the " - "provided types."); - const V saturated = CommonMaxOrMin<V>(!IsValueNegative(y)); - V result = {}; - return PA_BASE_NUMERICS_LIKELY((CheckedSubOp<T, U>::Do(x, y, &result))) - ? result - : saturated; - } -}; - -template <typename T, typename U, class Enable = void> -struct ClampedMulOp {}; - -template <typename T, typename U> -struct ClampedMulOp<T, - U, - typename std::enable_if<std::is_integral<T>::value && - std::is_integral<U>::value>::type> { - using result_type = typename MaxExponentPromotion<T, U>::type; - template <typename V = result_type> - static constexpr V Do(T x, U y) { - if (!PA_IsConstantEvaluated() && ClampedMulFastOp<T, U>::is_supported) - return ClampedMulFastOp<T, U>::template Do<V>(x, y); - - V result = {}; - const V saturated = - CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y)); - return PA_BASE_NUMERICS_LIKELY((CheckedMulOp<T, U>::Do(x, y, &result))) - ? result - : saturated; - } -}; - -template <typename T, typename U, class Enable = void> -struct ClampedDivOp {}; - -template <typename T, typename U> -struct ClampedDivOp<T, - U, - typename std::enable_if<std::is_integral<T>::value && - std::is_integral<U>::value>::type> { - using result_type = typename MaxExponentPromotion<T, U>::type; - template <typename V = result_type> - static constexpr V Do(T x, U y) { - V result = {}; - if (PA_BASE_NUMERICS_LIKELY((CheckedDivOp<T, U>::Do(x, y, &result)))) - return result; - // Saturation goes to max, min, or NaN (if x is zero). - return x ? CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y)) - : SaturationDefaultLimits<V>::NaN(); - } -}; - -template <typename T, typename U, class Enable = void> -struct ClampedModOp {}; - -template <typename T, typename U> -struct ClampedModOp<T, - U, - typename std::enable_if<std::is_integral<T>::value && - std::is_integral<U>::value>::type> { - using result_type = typename MaxExponentPromotion<T, U>::type; - template <typename V = result_type> - static constexpr V Do(T x, U y) { - V result = {}; - return PA_BASE_NUMERICS_LIKELY((CheckedModOp<T, U>::Do(x, y, &result))) - ? result - : x; - } -}; - -template <typename T, typename U, class Enable = void> -struct ClampedLshOp {}; - -// Left shift. Non-zero values saturate in the direction of the sign. A zero -// shifted by any value always results in zero. -template <typename T, typename U> -struct ClampedLshOp<T, - U, - typename std::enable_if<std::is_integral<T>::value && - std::is_integral<U>::value>::type> { - using result_type = T; - template <typename V = result_type> - static constexpr V Do(T x, U shift) { - static_assert(!std::is_signed<U>::value, "Shift value must be unsigned."); - if (PA_BASE_NUMERICS_LIKELY(shift < std::numeric_limits<T>::digits)) { - // Shift as unsigned to avoid undefined behavior. - V result = static_cast<V>(as_unsigned(x) << shift); - // If the shift can be reversed, we know it was valid. - if (PA_BASE_NUMERICS_LIKELY(result >> shift == x)) - return result; - } - return x ? CommonMaxOrMin<V>(IsValueNegative(x)) : 0; - } -}; - -template <typename T, typename U, class Enable = void> -struct ClampedRshOp {}; - -// Right shift. Negative values saturate to -1. Positive or 0 saturates to 0. -template <typename T, typename U> -struct ClampedRshOp<T, - U, - typename std::enable_if<std::is_integral<T>::value && - std::is_integral<U>::value>::type> { - using result_type = T; - template <typename V = result_type> - static constexpr V Do(T x, U shift) { - static_assert(!std::is_signed<U>::value, "Shift value must be unsigned."); - // Signed right shift is odd, because it saturates to -1 or 0. - const V saturated = as_unsigned(V(0)) - IsValueNegative(x); - return PA_BASE_NUMERICS_LIKELY(shift < IntegerBitsPlusSign<T>::value) - ? saturated_cast<V>(x >> shift) - : saturated; - } -}; - -template <typename T, typename U, class Enable = void> -struct ClampedAndOp {}; - -template <typename T, typename U> -struct ClampedAndOp<T, - U, - typename std::enable_if<std::is_integral<T>::value && - std::is_integral<U>::value>::type> { - using result_type = typename std::make_unsigned< - typename MaxExponentPromotion<T, U>::type>::type; - template <typename V> - static constexpr V Do(T x, U y) { - return static_cast<result_type>(x) & static_cast<result_type>(y); - } -}; - -template <typename T, typename U, class Enable = void> -struct ClampedOrOp {}; - -// For simplicity we promote to unsigned integers. -template <typename T, typename U> -struct ClampedOrOp<T, - U, - typename std::enable_if<std::is_integral<T>::value && - std::is_integral<U>::value>::type> { - using result_type = typename std::make_unsigned< - typename MaxExponentPromotion<T, U>::type>::type; - template <typename V> - static constexpr V Do(T x, U y) { - return static_cast<result_type>(x) | static_cast<result_type>(y); - } -}; - -template <typename T, typename U, class Enable = void> -struct ClampedXorOp {}; - -// For simplicity we support only unsigned integers. -template <typename T, typename U> -struct ClampedXorOp<T, - U, - typename std::enable_if<std::is_integral<T>::value && - std::is_integral<U>::value>::type> { - using result_type = typename std::make_unsigned< - typename MaxExponentPromotion<T, U>::type>::type; - template <typename V> - static constexpr V Do(T x, U y) { - return static_cast<result_type>(x) ^ static_cast<result_type>(y); - } -}; - -template <typename T, typename U, class Enable = void> -struct ClampedMaxOp {}; - -template <typename T, typename U> -struct ClampedMaxOp< - T, - U, - typename std::enable_if<std::is_arithmetic<T>::value && - std::is_arithmetic<U>::value>::type> { - using result_type = typename MaxExponentPromotion<T, U>::type; - template <typename V = result_type> - static constexpr V Do(T x, U y) { - return IsGreater<T, U>::Test(x, y) ? saturated_cast<V>(x) - : saturated_cast<V>(y); - } -}; - -template <typename T, typename U, class Enable = void> -struct ClampedMinOp {}; - -template <typename T, typename U> -struct ClampedMinOp< - T, - U, - typename std::enable_if<std::is_arithmetic<T>::value && - std::is_arithmetic<U>::value>::type> { - using result_type = typename LowestValuePromotion<T, U>::type; - template <typename V = result_type> - static constexpr V Do(T x, U y) { - return IsLess<T, U>::Test(x, y) ? saturated_cast<V>(x) - : saturated_cast<V>(y); - } -}; - -// This is just boilerplate that wraps the standard floating point arithmetic. -// A macro isn't the nicest solution, but it beats rewriting these repeatedly. -#define PA_BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \ - template <typename T, typename U> \ - struct Clamped##NAME##Op< \ - T, U, \ - typename std::enable_if<std::is_floating_point<T>::value || \ - std::is_floating_point<U>::value>::type> { \ - using result_type = typename MaxExponentPromotion<T, U>::type; \ - template <typename V = result_type> \ - static constexpr V Do(T x, U y) { \ - return saturated_cast<V>(x OP y); \ - } \ - }; - -PA_BASE_FLOAT_ARITHMETIC_OPS(Add, +) -PA_BASE_FLOAT_ARITHMETIC_OPS(Sub, -) -PA_BASE_FLOAT_ARITHMETIC_OPS(Mul, *) -PA_BASE_FLOAT_ARITHMETIC_OPS(Div, /) - -#undef PA_BASE_FLOAT_ARITHMETIC_OPS - -} // namespace partition_alloc::internal::base::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/numerics/math_constants.h b/base/allocator/partition_allocator/partition_alloc_base/numerics/math_constants.h deleted file mode 100644 index da7b4dd..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/numerics/math_constants.h +++ /dev/null
@@ -1,19 +0,0 @@ -// Copyright 2017 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_MATH_CONSTANTS_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_MATH_CONSTANTS_H_ - -namespace partition_alloc::internal::base { - -constexpr double kPiDouble = 3.14159265358979323846; -constexpr float kPiFloat = 3.14159265358979323846f; - -// The mean acceleration due to gravity on Earth in m/s^2. -constexpr double kMeanGravityDouble = 9.80665; -constexpr float kMeanGravityFloat = 9.80665f; - -} // namespace partition_alloc::internal::base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_MATH_CONSTANTS_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/numerics/ostream_operators.h b/base/allocator/partition_allocator/partition_alloc_base/numerics/ostream_operators.h deleted file mode 100644 index 87634bc..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/numerics/ostream_operators.h +++ /dev/null
@@ -1,33 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_OSTREAM_OPERATORS_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_OSTREAM_OPERATORS_H_ - -#include <ostream> - -namespace partition_alloc::internal::base::internal { - -template <typename T> -class ClampedNumeric; -template <typename T> -class StrictNumeric; - -// Overload the ostream output operator to make logging work nicely. -template <typename T> -std::ostream& operator<<(std::ostream& os, const StrictNumeric<T>& value) { - os << static_cast<T>(value); - return os; -} - -// Overload the ostream output operator to make logging work nicely. -template <typename T> -std::ostream& operator<<(std::ostream& os, const ClampedNumeric<T>& value) { - os << static_cast<T>(value); - return os; -} - -} // namespace partition_alloc::internal::base::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_OSTREAM_OPERATORS_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/numerics/ranges.h b/base/allocator/partition_allocator/partition_alloc_base/numerics/ranges.h deleted file mode 100644 index 0de0a38..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/numerics/ranges.h +++ /dev/null
@@ -1,21 +0,0 @@ -// Copyright 2017 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_RANGES_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_RANGES_H_ - -#include <cmath> -#include <type_traits> - -namespace partition_alloc::internal::base { - -template <typename T> -constexpr bool IsApproximatelyEqual(T lhs, T rhs, T tolerance) { - static_assert(std::is_arithmetic<T>::value, "Argument must be arithmetic"); - return std::abs(rhs - lhs) <= tolerance; -} - -} // namespace partition_alloc::internal::base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_RANGES_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/numerics/safe_conversions.h b/base/allocator/partition_allocator/partition_alloc_base/numerics/safe_conversions.h deleted file mode 100644 index 9d65355..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/numerics/safe_conversions.h +++ /dev/null
@@ -1,381 +0,0 @@ -// Copyright 2014 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_SAFE_CONVERSIONS_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_SAFE_CONVERSIONS_H_ - -#include <stddef.h> - -#include <cmath> -#include <limits> -#include <type_traits> - -#include "base/allocator/partition_allocator/partition_alloc_base/numerics/safe_conversions_impl.h" - -#if defined(__ARMEL__) && !defined(__native_client__) -#include "base/allocator/partition_allocator/partition_alloc_base/numerics/safe_conversions_arm_impl.h" -#define PA_BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (1) -#else -#define PA_BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (0) -#endif - -#if !PA_BASE_NUMERICS_DISABLE_OSTREAM_OPERATORS -#include <ostream> -#endif - -namespace partition_alloc::internal::base { -namespace internal { - -#if !PA_BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS -template <typename Dst, typename Src> -struct SaturateFastAsmOp { - static constexpr bool is_supported = false; - static constexpr Dst Do(Src) { - // Force a compile failure if instantiated. - return CheckOnFailure::template HandleFailure<Dst>(); - } -}; -#endif // PA_BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS -#undef PA_BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS - -// The following special case a few specific integer conversions where we can -// eke out better performance than range checking. -template <typename Dst, typename Src, typename Enable = void> -struct IsValueInRangeFastOp { - static constexpr bool is_supported = false; - static constexpr bool Do(Src value) { - // Force a compile failure if instantiated. - return CheckOnFailure::template HandleFailure<bool>(); - } -}; - -// Signed to signed range comparison. -template <typename Dst, typename Src> -struct IsValueInRangeFastOp< - Dst, - Src, - typename std::enable_if< - std::is_integral<Dst>::value && std::is_integral<Src>::value && - std::is_signed<Dst>::value && std::is_signed<Src>::value && - !IsTypeInRangeForNumericType<Dst, Src>::value>::type> { - static constexpr bool is_supported = true; - - static constexpr bool Do(Src value) { - // Just downcast to the smaller type, sign extend it back to the original - // type, and then see if it matches the original value. - return value == static_cast<Dst>(value); - } -}; - -// Signed to unsigned range comparison. -template <typename Dst, typename Src> -struct IsValueInRangeFastOp< - Dst, - Src, - typename std::enable_if< - std::is_integral<Dst>::value && std::is_integral<Src>::value && - !std::is_signed<Dst>::value && std::is_signed<Src>::value && - !IsTypeInRangeForNumericType<Dst, Src>::value>::type> { - static constexpr bool is_supported = true; - - static constexpr bool Do(Src value) { - // We cast a signed as unsigned to overflow negative values to the top, - // then compare against whichever maximum is smaller, as our upper bound. - return as_unsigned(value) <= as_unsigned(CommonMax<Src, Dst>()); - } -}; - -// Convenience function that returns true if the supplied value is in range -// for the destination type. -template <typename Dst, typename Src> -constexpr bool IsValueInRangeForNumericType(Src value) { - using SrcType = typename internal::UnderlyingType<Src>::type; - return internal::IsValueInRangeFastOp<Dst, SrcType>::is_supported - ? internal::IsValueInRangeFastOp<Dst, SrcType>::Do( - static_cast<SrcType>(value)) - : internal::DstRangeRelationToSrcRange<Dst>( - static_cast<SrcType>(value)) - .IsValid(); -} - -// checked_cast<> is analogous to static_cast<> for numeric types, -// except that it CHECKs that the specified numeric conversion will not -// overflow or underflow. NaN source will always trigger a CHECK. -template <typename Dst, - class CheckHandler = internal::CheckOnFailure, - typename Src> -constexpr Dst checked_cast(Src value) { - // This throws a compile-time error on evaluating the constexpr if it can be - // determined at compile-time as failing, otherwise it will CHECK at runtime. - using SrcType = typename internal::UnderlyingType<Src>::type; - return PA_BASE_NUMERICS_LIKELY((IsValueInRangeForNumericType<Dst>(value))) - ? static_cast<Dst>(static_cast<SrcType>(value)) - : CheckHandler::template HandleFailure<Dst>(); -} - -// Default boundaries for integral/float: max/infinity, lowest/-infinity, 0/NaN. -// You may provide your own limits (e.g. to saturated_cast) so long as you -// implement all of the static constexpr member functions in the class below. -template <typename T> -struct SaturationDefaultLimits : public std::numeric_limits<T> { - static constexpr T NaN() { - return std::numeric_limits<T>::has_quiet_NaN - ? std::numeric_limits<T>::quiet_NaN() - : T(); - } - using std::numeric_limits<T>::max; - static constexpr T Overflow() { - return std::numeric_limits<T>::has_infinity - ? std::numeric_limits<T>::infinity() - : std::numeric_limits<T>::max(); - } - using std::numeric_limits<T>::lowest; - static constexpr T Underflow() { - return std::numeric_limits<T>::has_infinity - ? std::numeric_limits<T>::infinity() * -1 - : std::numeric_limits<T>::lowest(); - } -}; - -template <typename Dst, template <typename> class S, typename Src> -constexpr Dst saturated_cast_impl(Src value, RangeCheck constraint) { - // For some reason clang generates much better code when the branch is - // structured exactly this way, rather than a sequence of checks. - return !constraint.IsOverflowFlagSet() - ? (!constraint.IsUnderflowFlagSet() ? static_cast<Dst>(value) - : S<Dst>::Underflow()) - // Skip this check for integral Src, which cannot be NaN. - : (std::is_integral<Src>::value || !constraint.IsUnderflowFlagSet() - ? S<Dst>::Overflow() - : S<Dst>::NaN()); -} - -// We can reduce the number of conditions and get slightly better performance -// for normal signed and unsigned integer ranges. And in the specific case of -// Arm, we can use the optimized saturation instructions. -template <typename Dst, typename Src, typename Enable = void> -struct SaturateFastOp { - static constexpr bool is_supported = false; - static constexpr Dst Do(Src value) { - // Force a compile failure if instantiated. - return CheckOnFailure::template HandleFailure<Dst>(); - } -}; - -template <typename Dst, typename Src> -struct SaturateFastOp< - Dst, - Src, - typename std::enable_if<std::is_integral<Src>::value && - std::is_integral<Dst>::value && - SaturateFastAsmOp<Dst, Src>::is_supported>::type> { - static constexpr bool is_supported = true; - static constexpr Dst Do(Src value) { - return SaturateFastAsmOp<Dst, Src>::Do(value); - } -}; - -template <typename Dst, typename Src> -struct SaturateFastOp< - Dst, - Src, - typename std::enable_if<std::is_integral<Src>::value && - std::is_integral<Dst>::value && - !SaturateFastAsmOp<Dst, Src>::is_supported>::type> { - static constexpr bool is_supported = true; - static constexpr Dst Do(Src value) { - // The exact order of the following is structured to hit the correct - // optimization heuristics across compilers. Do not change without - // checking the emitted code. - const Dst saturated = CommonMaxOrMin<Dst, Src>( - IsMaxInRangeForNumericType<Dst, Src>() || - (!IsMinInRangeForNumericType<Dst, Src>() && IsValueNegative(value))); - return PA_BASE_NUMERICS_LIKELY(IsValueInRangeForNumericType<Dst>(value)) - ? static_cast<Dst>(value) - : saturated; - } -}; - -// saturated_cast<> is analogous to static_cast<> for numeric types, except -// that the specified numeric conversion will saturate by default rather than -// overflow or underflow, and NaN assignment to an integral will return 0. -// All boundary condition behaviors can be overridden with a custom handler. -template <typename Dst, - template <typename> class SaturationHandler = SaturationDefaultLimits, - typename Src> -constexpr Dst saturated_cast(Src value) { - using SrcType = typename UnderlyingType<Src>::type; - return !PA_IsConstantEvaluated() && - SaturateFastOp<Dst, SrcType>::is_supported && - std::is_same<SaturationHandler<Dst>, - SaturationDefaultLimits<Dst>>::value - ? SaturateFastOp<Dst, SrcType>::Do(static_cast<SrcType>(value)) - : saturated_cast_impl<Dst, SaturationHandler, SrcType>( - static_cast<SrcType>(value), - DstRangeRelationToSrcRange<Dst, SaturationHandler, SrcType>( - static_cast<SrcType>(value))); -} - -// strict_cast<> is analogous to static_cast<> for numeric types, except that -// it will cause a compile failure if the destination type is not large enough -// to contain any value in the source type. It performs no runtime checking. -template <typename Dst, typename Src> -constexpr Dst strict_cast(Src value) { - using SrcType = typename UnderlyingType<Src>::type; - static_assert(UnderlyingType<Src>::is_numeric, "Argument must be numeric."); - static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric."); - - // If you got here from a compiler error, it's because you tried to assign - // from a source type to a destination type that has insufficient range. - // The solution may be to change the destination type you're assigning to, - // and use one large enough to represent the source. - // Alternatively, you may be better served with the checked_cast<> or - // saturated_cast<> template functions for your particular use case. - static_assert(StaticDstRangeRelationToSrcRange<Dst, SrcType>::value == - NUMERIC_RANGE_CONTAINED, - "The source type is out of range for the destination type. " - "Please see strict_cast<> comments for more information."); - - return static_cast<Dst>(static_cast<SrcType>(value)); -} - -// Some wrappers to statically check that a type is in range. -template <typename Dst, typename Src, class Enable = void> -struct IsNumericRangeContained { - static constexpr bool value = false; -}; - -template <typename Dst, typename Src> -struct IsNumericRangeContained< - Dst, - Src, - typename std::enable_if<ArithmeticOrUnderlyingEnum<Dst>::value && - ArithmeticOrUnderlyingEnum<Src>::value>::type> { - static constexpr bool value = - StaticDstRangeRelationToSrcRange<Dst, Src>::value == - NUMERIC_RANGE_CONTAINED; -}; - -// StrictNumeric implements compile time range checking between numeric types by -// wrapping assignment operations in a strict_cast. This class is intended to be -// used for function arguments and return types, to ensure the destination type -// can always contain the source type. This is essentially the same as enforcing -// -Wconversion in gcc and C4302 warnings on MSVC, but it can be applied -// incrementally at API boundaries, making it easier to convert code so that it -// compiles cleanly with truncation warnings enabled. -// This template should introduce no runtime overhead, but it also provides no -// runtime checking of any of the associated mathematical operations. Use -// CheckedNumeric for runtime range checks of the actual value being assigned. -template <typename T> -class StrictNumeric { - public: - using type = T; - - constexpr StrictNumeric() : value_(0) {} - - // Copy constructor. - template <typename Src> - constexpr StrictNumeric(const StrictNumeric<Src>& rhs) - : value_(strict_cast<T>(rhs.value_)) {} - - // This is not an explicit constructor because we implicitly upgrade regular - // numerics to StrictNumerics to make them easier to use. - template <typename Src> - constexpr StrictNumeric(Src value) // NOLINT(runtime/explicit) - : value_(strict_cast<T>(value)) {} - - // If you got here from a compiler error, it's because you tried to assign - // from a source type to a destination type that has insufficient range. - // The solution may be to change the destination type you're assigning to, - // and use one large enough to represent the source. - // If you're assigning from a CheckedNumeric<> class, you may be able to use - // the AssignIfValid() member function, specify a narrower destination type to - // the member value functions (e.g. val.template ValueOrDie<Dst>()), use one - // of the value helper functions (e.g. ValueOrDieForType<Dst>(val)). - // If you've encountered an _ambiguous overload_ you can use a static_cast<> - // to explicitly cast the result to the destination type. - // If none of that works, you may be better served with the checked_cast<> or - // saturated_cast<> template functions for your particular use case. - template <typename Dst, - typename std::enable_if< - IsNumericRangeContained<Dst, T>::value>::type* = nullptr> - constexpr operator Dst() const { - return static_cast<typename ArithmeticOrUnderlyingEnum<Dst>::type>(value_); - } - - private: - const T value_; -}; - -// Convenience wrapper returns a StrictNumeric from the provided arithmetic -// type. -template <typename T> -constexpr StrictNumeric<typename UnderlyingType<T>::type> MakeStrictNum( - const T value) { - return value; -} - -#define PA_BASE_NUMERIC_COMPARISON_OPERATORS(CLASS, NAME, OP) \ - template <typename L, typename R, \ - typename std::enable_if< \ - internal::Is##CLASS##Op<L, R>::value>::type* = nullptr> \ - constexpr bool operator OP(const L lhs, const R rhs) { \ - return SafeCompare<NAME, typename UnderlyingType<L>::type, \ - typename UnderlyingType<R>::type>(lhs, rhs); \ - } - -PA_BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLess, <) -PA_BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLessOrEqual, <=) -PA_BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreater, >) -PA_BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreaterOrEqual, >=) -PA_BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsEqual, ==) -PA_BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsNotEqual, !=) - -} // namespace internal - -using internal::as_signed; -using internal::as_unsigned; -using internal::checked_cast; -using internal::IsTypeInRangeForNumericType; -using internal::IsValueInRangeForNumericType; -using internal::IsValueNegative; -using internal::MakeStrictNum; -using internal::SafeUnsignedAbs; -using internal::saturated_cast; -using internal::strict_cast; -using internal::StrictNumeric; - -// Explicitly make a shorter size_t alias for convenience. -using SizeT = StrictNumeric<size_t>; - -// floating -> integral conversions that saturate and thus can actually return -// an integral type. In most cases, these should be preferred over the std:: -// versions. -template <typename Dst = int, - typename Src, - typename = std::enable_if_t<std::is_integral<Dst>::value && - std::is_floating_point<Src>::value>> -Dst ClampFloor(Src value) { - return saturated_cast<Dst>(std::floor(value)); -} -template <typename Dst = int, - typename Src, - typename = std::enable_if_t<std::is_integral<Dst>::value && - std::is_floating_point<Src>::value>> -Dst ClampCeil(Src value) { - return saturated_cast<Dst>(std::ceil(value)); -} -template <typename Dst = int, - typename Src, - typename = std::enable_if_t<std::is_integral<Dst>::value && - std::is_floating_point<Src>::value>> -Dst ClampRound(Src value) { - const Src rounded = - (value >= 0.0f) ? std::floor(value + 0.5f) : std::ceil(value - 0.5f); - return saturated_cast<Dst>(rounded); -} - -} // namespace partition_alloc::internal::base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_SAFE_CONVERSIONS_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/numerics/safe_conversions_arm_impl.h b/base/allocator/partition_allocator/partition_alloc_base/numerics/safe_conversions_arm_impl.h deleted file mode 100644 index b23e42f..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/numerics/safe_conversions_arm_impl.h +++ /dev/null
@@ -1,49 +0,0 @@ -// Copyright 2017 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_SAFE_CONVERSIONS_ARM_IMPL_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_SAFE_CONVERSIONS_ARM_IMPL_H_ - -#include <cassert> -#include <limits> -#include <type_traits> - -#include "base/allocator/partition_allocator/partition_alloc_base/numerics/safe_conversions_impl.h" - -namespace partition_alloc::internal::base::internal { - -// Fast saturation to a destination type. -template <typename Dst, typename Src> -struct SaturateFastAsmOp { - static constexpr bool is_supported = - kEnableAsmCode && std::is_signed<Src>::value && - std::is_integral<Dst>::value && std::is_integral<Src>::value && - IntegerBitsPlusSign<Src>::value <= IntegerBitsPlusSign<int32_t>::value && - IntegerBitsPlusSign<Dst>::value <= IntegerBitsPlusSign<int32_t>::value && - !IsTypeInRangeForNumericType<Dst, Src>::value; - - __attribute__((always_inline)) static Dst Do(Src value) { - int32_t src = value; - typename std::conditional<std::is_signed<Dst>::value, int32_t, - uint32_t>::type result; - if (std::is_signed<Dst>::value) { - asm("ssat %[dst], %[shift], %[src]" - : [dst] "=r"(result) - : [src] "r"(src), [shift] "n"(IntegerBitsPlusSign<Dst>::value <= 32 - ? IntegerBitsPlusSign<Dst>::value - : 32)); - } else { - asm("usat %[dst], %[shift], %[src]" - : [dst] "=r"(result) - : [src] "r"(src), [shift] "n"(IntegerBitsPlusSign<Dst>::value < 32 - ? IntegerBitsPlusSign<Dst>::value - : 31)); - } - return static_cast<Dst>(result); - } -}; - -} // namespace partition_alloc::internal::base::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_SAFE_CONVERSIONS_ARM_IMPL_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/numerics/safe_conversions_impl.h b/base/allocator/partition_allocator/partition_alloc_base/numerics/safe_conversions_impl.h deleted file mode 100644 index 3767074..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/numerics/safe_conversions_impl.h +++ /dev/null
@@ -1,845 +0,0 @@ -// Copyright 2014 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ - -#include <stdint.h> - -#include <limits> -#include <type_traits> - -#if defined(__GNUC__) || defined(__clang__) -#define PA_BASE_NUMERICS_LIKELY(x) __builtin_expect(!!(x), 1) -#define PA_BASE_NUMERICS_UNLIKELY(x) __builtin_expect(!!(x), 0) -#else -#define PA_BASE_NUMERICS_LIKELY(x) (x) -#define PA_BASE_NUMERICS_UNLIKELY(x) (x) -#endif - -namespace partition_alloc::internal::base::internal { - -// The std library doesn't provide a binary max_exponent for integers, however -// we can compute an analog using std::numeric_limits<>::digits. -template <typename NumericType> -struct MaxExponent { - static const int value = std::is_floating_point<NumericType>::value - ? std::numeric_limits<NumericType>::max_exponent - : std::numeric_limits<NumericType>::digits + 1; -}; - -// The number of bits (including the sign) in an integer. Eliminates sizeof -// hacks. -template <typename NumericType> -struct IntegerBitsPlusSign { - static const int value = std::numeric_limits<NumericType>::digits + - std::is_signed<NumericType>::value; -}; - -// Helper templates for integer manipulations. - -template <typename Integer> -struct PositionOfSignBit { - static const size_t value = IntegerBitsPlusSign<Integer>::value - 1; -}; - -// Determines if a numeric value is negative without throwing compiler -// warnings on: unsigned(value) < 0. -template <typename T, - typename std::enable_if<std::is_signed<T>::value>::type* = nullptr> -constexpr bool IsValueNegative(T value) { - static_assert(std::is_arithmetic<T>::value, "Argument must be numeric."); - return value < 0; -} - -template <typename T, - typename std::enable_if<!std::is_signed<T>::value>::type* = nullptr> -constexpr bool IsValueNegative(T) { - static_assert(std::is_arithmetic<T>::value, "Argument must be numeric."); - return false; -} - -// This performs a fast negation, returning a signed value. It works on unsigned -// arguments, but probably doesn't do what you want for any unsigned value -// larger than max / 2 + 1 (i.e. signed min cast to unsigned). -template <typename T> -constexpr typename std::make_signed<T>::type ConditionalNegate( - T x, - bool is_negative) { - static_assert(std::is_integral<T>::value, "Type must be integral"); - using SignedT = typename std::make_signed<T>::type; - using UnsignedT = typename std::make_unsigned<T>::type; - return static_cast<SignedT>((static_cast<UnsignedT>(x) ^ - static_cast<UnsignedT>(-SignedT(is_negative))) + - is_negative); -} - -// This performs a safe, absolute value via unsigned overflow. -template <typename T> -constexpr typename std::make_unsigned<T>::type SafeUnsignedAbs(T value) { - static_assert(std::is_integral<T>::value, "Type must be integral"); - using UnsignedT = typename std::make_unsigned<T>::type; - return IsValueNegative(value) - ? static_cast<UnsignedT>(0u - static_cast<UnsignedT>(value)) - : static_cast<UnsignedT>(value); -} - -// TODO(jschuh): Switch to std::is_constant_evaluated() once C++20 is supported. -// Alternately, the usage could be restructured for "consteval if" in C++23. -#define PA_IsConstantEvaluated() (__builtin_is_constant_evaluated()) - -// TODO(jschuh): Debug builds don't reliably propagate constants, so we restrict -// some accelerated runtime paths to release builds until this can be forced -// with consteval support in C++20 or C++23. -#if defined(NDEBUG) -constexpr bool kEnableAsmCode = true; -#else -constexpr bool kEnableAsmCode = false; -#endif - -// Forces a crash, like a CHECK(false). Used for numeric boundary errors. -// Also used in a constexpr template to trigger a compilation failure on -// an error condition. -struct CheckOnFailure { - template <typename T> - static T HandleFailure() { -#if defined(_MSC_VER) - __debugbreak(); -#elif defined(__GNUC__) || defined(__clang__) - __builtin_trap(); -#else - ((void)(*(volatile char*)0 = 0)); -#endif - return T(); - } -}; - -enum IntegerRepresentation { - INTEGER_REPRESENTATION_UNSIGNED, - INTEGER_REPRESENTATION_SIGNED -}; - -// A range for a given nunmeric Src type is contained for a given numeric Dst -// type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and -// numeric_limits<Src>::lowest() >= numeric_limits<Dst>::lowest() are true. -// We implement this as template specializations rather than simple static -// comparisons to ensure type correctness in our comparisons. -enum NumericRangeRepresentation { - NUMERIC_RANGE_NOT_CONTAINED, - NUMERIC_RANGE_CONTAINED -}; - -// Helper templates to statically determine if our destination type can contain -// maximum and minimum values represented by the source type. - -template <typename Dst, - typename Src, - IntegerRepresentation DstSign = std::is_signed<Dst>::value - ? INTEGER_REPRESENTATION_SIGNED - : INTEGER_REPRESENTATION_UNSIGNED, - IntegerRepresentation SrcSign = std::is_signed<Src>::value - ? INTEGER_REPRESENTATION_SIGNED - : INTEGER_REPRESENTATION_UNSIGNED> -struct StaticDstRangeRelationToSrcRange; - -// Same sign: Dst is guaranteed to contain Src only if its range is equal or -// larger. -template <typename Dst, typename Src, IntegerRepresentation Sign> -struct StaticDstRangeRelationToSrcRange<Dst, Src, Sign, Sign> { - static const NumericRangeRepresentation value = - MaxExponent<Dst>::value >= MaxExponent<Src>::value - ? NUMERIC_RANGE_CONTAINED - : NUMERIC_RANGE_NOT_CONTAINED; -}; - -// Unsigned to signed: Dst is guaranteed to contain source only if its range is -// larger. -template <typename Dst, typename Src> -struct StaticDstRangeRelationToSrcRange<Dst, - Src, - INTEGER_REPRESENTATION_SIGNED, - INTEGER_REPRESENTATION_UNSIGNED> { - static const NumericRangeRepresentation value = - MaxExponent<Dst>::value > MaxExponent<Src>::value - ? NUMERIC_RANGE_CONTAINED - : NUMERIC_RANGE_NOT_CONTAINED; -}; - -// Signed to unsigned: Dst cannot be statically determined to contain Src. -template <typename Dst, typename Src> -struct StaticDstRangeRelationToSrcRange<Dst, - Src, - INTEGER_REPRESENTATION_UNSIGNED, - INTEGER_REPRESENTATION_SIGNED> { - static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED; -}; - -// This class wraps the range constraints as separate booleans so the compiler -// can identify constants and eliminate unused code paths. -class RangeCheck { - public: - constexpr RangeCheck(bool is_in_lower_bound, bool is_in_upper_bound) - : is_underflow_(!is_in_lower_bound), is_overflow_(!is_in_upper_bound) {} - constexpr RangeCheck() : is_underflow_(false), is_overflow_(false) {} - constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; } - constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; } - constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; } - constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; } - constexpr bool IsOverflowFlagSet() const { return is_overflow_; } - constexpr bool IsUnderflowFlagSet() const { return is_underflow_; } - constexpr bool operator==(const RangeCheck rhs) const { - return is_underflow_ == rhs.is_underflow_ && - is_overflow_ == rhs.is_overflow_; - } - constexpr bool operator!=(const RangeCheck rhs) const { - return !(*this == rhs); - } - - private: - // Do not change the order of these member variables. The integral conversion - // optimization depends on this exact order. - const bool is_underflow_; - const bool is_overflow_; -}; - -// The following helper template addresses a corner case in range checks for -// conversion from a floating-point type to an integral type of smaller range -// but larger precision (e.g. float -> unsigned). The problem is as follows: -// 1. Integral maximum is always one less than a power of two, so it must be -// truncated to fit the mantissa of the floating point. The direction of -// rounding is implementation defined, but by default it's always IEEE -// floats, which round to nearest and thus result in a value of larger -// magnitude than the integral value. -// Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX -// // is 4294967295u. -// 2. If the floating point value is equal to the promoted integral maximum -// value, a range check will erroneously pass. -// Example: (4294967296f <= 4294967295u) // This is true due to a precision -// // loss in rounding up to float. -// 3. When the floating point value is then converted to an integral, the -// resulting value is out of range for the target integral type and -// thus is implementation defined. -// Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0. -// To fix this bug we manually truncate the maximum value when the destination -// type is an integral of larger precision than the source floating-point type, -// such that the resulting maximum is represented exactly as a floating point. -template <typename Dst, typename Src, template <typename> class Bounds> -struct NarrowingRange { - using SrcLimits = std::numeric_limits<Src>; - using DstLimits = typename std::numeric_limits<Dst>; - - // Computes the mask required to make an accurate comparison between types. - static const int kShift = - (MaxExponent<Src>::value > MaxExponent<Dst>::value && - SrcLimits::digits < DstLimits::digits) - ? (DstLimits::digits - SrcLimits::digits) - : 0; - template < - typename T, - typename std::enable_if<std::is_integral<T>::value>::type* = nullptr> - - // Masks out the integer bits that are beyond the precision of the - // intermediate type used for comparison. - static constexpr T Adjust(T value) { - static_assert(std::is_same<T, Dst>::value, ""); - static_assert(kShift < DstLimits::digits, ""); - using UnsignedDst = typename std::make_unsigned_t<T>; - return static_cast<T>(ConditionalNegate( - SafeUnsignedAbs(value) & ~((UnsignedDst{1} << kShift) - UnsignedDst{1}), - IsValueNegative(value))); - } - - template <typename T, - typename std::enable_if<std::is_floating_point<T>::value>::type* = - nullptr> - static constexpr T Adjust(T value) { - static_assert(std::is_same<T, Dst>::value, ""); - static_assert(kShift == 0, ""); - return value; - } - - static constexpr Dst max() { return Adjust(Bounds<Dst>::max()); } - static constexpr Dst lowest() { return Adjust(Bounds<Dst>::lowest()); } -}; - -template <typename Dst, - typename Src, - template <typename> - class Bounds, - IntegerRepresentation DstSign = std::is_signed<Dst>::value - ? INTEGER_REPRESENTATION_SIGNED - : INTEGER_REPRESENTATION_UNSIGNED, - IntegerRepresentation SrcSign = std::is_signed<Src>::value - ? INTEGER_REPRESENTATION_SIGNED - : INTEGER_REPRESENTATION_UNSIGNED, - NumericRangeRepresentation DstRange = - StaticDstRangeRelationToSrcRange<Dst, Src>::value> -struct DstRangeRelationToSrcRangeImpl; - -// The following templates are for ranges that must be verified at runtime. We -// split it into checks based on signedness to avoid confusing casts and -// compiler warnings on signed an unsigned comparisons. - -// Same sign narrowing: The range is contained for normal limits. -template <typename Dst, - typename Src, - template <typename> - class Bounds, - IntegerRepresentation DstSign, - IntegerRepresentation SrcSign> -struct DstRangeRelationToSrcRangeImpl<Dst, - Src, - Bounds, - DstSign, - SrcSign, - NUMERIC_RANGE_CONTAINED> { - static constexpr RangeCheck Check(Src value) { - using SrcLimits = std::numeric_limits<Src>; - using DstLimits = NarrowingRange<Dst, Src, Bounds>; - return RangeCheck( - static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() || - static_cast<Dst>(value) >= DstLimits::lowest(), - static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() || - static_cast<Dst>(value) <= DstLimits::max()); - } -}; - -// Signed to signed narrowing: Both the upper and lower boundaries may be -// exceeded for standard limits. -template <typename Dst, typename Src, template <typename> class Bounds> -struct DstRangeRelationToSrcRangeImpl<Dst, - Src, - Bounds, - INTEGER_REPRESENTATION_SIGNED, - INTEGER_REPRESENTATION_SIGNED, - NUMERIC_RANGE_NOT_CONTAINED> { - static constexpr RangeCheck Check(Src value) { - using DstLimits = NarrowingRange<Dst, Src, Bounds>; - return RangeCheck(value >= DstLimits::lowest(), value <= DstLimits::max()); - } -}; - -// Unsigned to unsigned narrowing: Only the upper bound can be exceeded for -// standard limits. -template <typename Dst, typename Src, template <typename> class Bounds> -struct DstRangeRelationToSrcRangeImpl<Dst, - Src, - Bounds, - INTEGER_REPRESENTATION_UNSIGNED, - INTEGER_REPRESENTATION_UNSIGNED, - NUMERIC_RANGE_NOT_CONTAINED> { - static constexpr RangeCheck Check(Src value) { - using DstLimits = NarrowingRange<Dst, Src, Bounds>; - return RangeCheck( - DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest(), - value <= DstLimits::max()); - } -}; - -// Unsigned to signed: Only the upper bound can be exceeded for standard limits. -template <typename Dst, typename Src, template <typename> class Bounds> -struct DstRangeRelationToSrcRangeImpl<Dst, - Src, - Bounds, - INTEGER_REPRESENTATION_SIGNED, - INTEGER_REPRESENTATION_UNSIGNED, - NUMERIC_RANGE_NOT_CONTAINED> { - static constexpr RangeCheck Check(Src value) { - using DstLimits = NarrowingRange<Dst, Src, Bounds>; - using Promotion = decltype(Src() + Dst()); - return RangeCheck(DstLimits::lowest() <= Dst(0) || - static_cast<Promotion>(value) >= - static_cast<Promotion>(DstLimits::lowest()), - static_cast<Promotion>(value) <= - static_cast<Promotion>(DstLimits::max())); - } -}; - -// Signed to unsigned: The upper boundary may be exceeded for a narrower Dst, -// and any negative value exceeds the lower boundary for standard limits. -template <typename Dst, typename Src, template <typename> class Bounds> -struct DstRangeRelationToSrcRangeImpl<Dst, - Src, - Bounds, - INTEGER_REPRESENTATION_UNSIGNED, - INTEGER_REPRESENTATION_SIGNED, - NUMERIC_RANGE_NOT_CONTAINED> { - static constexpr RangeCheck Check(Src value) { - using SrcLimits = std::numeric_limits<Src>; - using DstLimits = NarrowingRange<Dst, Src, Bounds>; - using Promotion = decltype(Src() + Dst()); - bool ge_zero = false; - // Converting floating-point to integer will discard fractional part, so - // values in (-1.0, -0.0) will truncate to 0 and fit in Dst. - if (std::is_floating_point<Src>::value) { - ge_zero = value > Src(-1); - } else { - ge_zero = value >= Src(0); - } - return RangeCheck( - ge_zero && (DstLimits::lowest() == 0 || - static_cast<Dst>(value) >= DstLimits::lowest()), - static_cast<Promotion>(SrcLimits::max()) <= - static_cast<Promotion>(DstLimits::max()) || - static_cast<Promotion>(value) <= - static_cast<Promotion>(DstLimits::max())); - } -}; - -// Simple wrapper for statically checking if a type's range is contained. -template <typename Dst, typename Src> -struct IsTypeInRangeForNumericType { - static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value == - NUMERIC_RANGE_CONTAINED; -}; - -template <typename Dst, - template <typename> class Bounds = std::numeric_limits, - typename Src> -constexpr RangeCheck DstRangeRelationToSrcRange(Src value) { - static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric."); - static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric."); - static_assert(Bounds<Dst>::lowest() < Bounds<Dst>::max(), ""); - return DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds>::Check(value); -} - -// Integer promotion templates used by the portable checked integer arithmetic. -template <size_t Size, bool IsSigned> -struct IntegerForDigitsAndSign; - -#define PA_INTEGER_FOR_DIGITS_AND_SIGN(I) \ - template <> \ - struct IntegerForDigitsAndSign<IntegerBitsPlusSign<I>::value, \ - std::is_signed<I>::value> { \ - using type = I; \ - } - -PA_INTEGER_FOR_DIGITS_AND_SIGN(int8_t); -PA_INTEGER_FOR_DIGITS_AND_SIGN(uint8_t); -PA_INTEGER_FOR_DIGITS_AND_SIGN(int16_t); -PA_INTEGER_FOR_DIGITS_AND_SIGN(uint16_t); -PA_INTEGER_FOR_DIGITS_AND_SIGN(int32_t); -PA_INTEGER_FOR_DIGITS_AND_SIGN(uint32_t); -PA_INTEGER_FOR_DIGITS_AND_SIGN(int64_t); -PA_INTEGER_FOR_DIGITS_AND_SIGN(uint64_t); -#undef PA_INTEGER_FOR_DIGITS_AND_SIGN - -// WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to -// support 128-bit math, then the ArithmeticPromotion template below will need -// to be updated (or more likely replaced with a decltype expression). -static_assert(IntegerBitsPlusSign<intmax_t>::value == 64, - "Max integer size not supported for this toolchain."); - -template <typename Integer, bool IsSigned = std::is_signed<Integer>::value> -struct TwiceWiderInteger { - using type = - typename IntegerForDigitsAndSign<IntegerBitsPlusSign<Integer>::value * 2, - IsSigned>::type; -}; - -enum ArithmeticPromotionCategory { - LEFT_PROMOTION, // Use the type of the left-hand argument. - RIGHT_PROMOTION // Use the type of the right-hand argument. -}; - -// Determines the type that can represent the largest positive value. -template <typename Lhs, - typename Rhs, - ArithmeticPromotionCategory Promotion = - (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value) - ? LEFT_PROMOTION - : RIGHT_PROMOTION> -struct MaxExponentPromotion; - -template <typename Lhs, typename Rhs> -struct MaxExponentPromotion<Lhs, Rhs, LEFT_PROMOTION> { - using type = Lhs; -}; - -template <typename Lhs, typename Rhs> -struct MaxExponentPromotion<Lhs, Rhs, RIGHT_PROMOTION> { - using type = Rhs; -}; - -// Determines the type that can represent the lowest arithmetic value. -template <typename Lhs, - typename Rhs, - ArithmeticPromotionCategory Promotion = - std::is_signed<Lhs>::value - ? (std::is_signed<Rhs>::value - ? (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value - ? LEFT_PROMOTION - : RIGHT_PROMOTION) - : LEFT_PROMOTION) - : (std::is_signed<Rhs>::value - ? RIGHT_PROMOTION - : (MaxExponent<Lhs>::value < MaxExponent<Rhs>::value - ? LEFT_PROMOTION - : RIGHT_PROMOTION))> -struct LowestValuePromotion; - -template <typename Lhs, typename Rhs> -struct LowestValuePromotion<Lhs, Rhs, LEFT_PROMOTION> { - using type = Lhs; -}; - -template <typename Lhs, typename Rhs> -struct LowestValuePromotion<Lhs, Rhs, RIGHT_PROMOTION> { - using type = Rhs; -}; - -// Determines the type that is best able to represent an arithmetic result. -template < - typename Lhs, - typename Rhs = Lhs, - bool is_intmax_type = - std::is_integral<typename MaxExponentPromotion<Lhs, Rhs>::type>::value&& - IntegerBitsPlusSign<typename MaxExponentPromotion<Lhs, Rhs>::type>:: - value == IntegerBitsPlusSign<intmax_t>::value, - bool is_max_exponent = - StaticDstRangeRelationToSrcRange< - typename MaxExponentPromotion<Lhs, Rhs>::type, - Lhs>::value == - NUMERIC_RANGE_CONTAINED&& StaticDstRangeRelationToSrcRange< - typename MaxExponentPromotion<Lhs, Rhs>::type, - Rhs>::value == NUMERIC_RANGE_CONTAINED> -struct BigEnoughPromotion; - -// The side with the max exponent is big enough. -template <typename Lhs, typename Rhs, bool is_intmax_type> -struct BigEnoughPromotion<Lhs, Rhs, is_intmax_type, true> { - using type = typename MaxExponentPromotion<Lhs, Rhs>::type; - static const bool is_contained = true; -}; - -// We can use a twice wider type to fit. -template <typename Lhs, typename Rhs> -struct BigEnoughPromotion<Lhs, Rhs, false, false> { - using type = - typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type, - std::is_signed<Lhs>::value || - std::is_signed<Rhs>::value>::type; - static const bool is_contained = true; -}; - -// No type is large enough. -template <typename Lhs, typename Rhs> -struct BigEnoughPromotion<Lhs, Rhs, true, false> { - using type = typename MaxExponentPromotion<Lhs, Rhs>::type; - static const bool is_contained = false; -}; - -// We can statically check if operations on the provided types can wrap, so we -// can skip the checked operations if they're not needed. So, for an integer we -// care if the destination type preserves the sign and is twice the width of -// the source. -template <typename T, typename Lhs, typename Rhs = Lhs> -struct IsIntegerArithmeticSafe { - static const bool value = - !std::is_floating_point<T>::value && - !std::is_floating_point<Lhs>::value && - !std::is_floating_point<Rhs>::value && - std::is_signed<T>::value >= std::is_signed<Lhs>::value && - IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Lhs>::value) && - std::is_signed<T>::value >= std::is_signed<Rhs>::value && - IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Rhs>::value); -}; - -// Promotes to a type that can represent any possible result of a binary -// arithmetic operation with the source types. -template <typename Lhs, - typename Rhs, - bool is_promotion_possible = IsIntegerArithmeticSafe< - typename std::conditional<std::is_signed<Lhs>::value || - std::is_signed<Rhs>::value, - intmax_t, - uintmax_t>::type, - typename MaxExponentPromotion<Lhs, Rhs>::type>::value> -struct FastIntegerArithmeticPromotion; - -template <typename Lhs, typename Rhs> -struct FastIntegerArithmeticPromotion<Lhs, Rhs, true> { - using type = - typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type, - std::is_signed<Lhs>::value || - std::is_signed<Rhs>::value>::type; - static_assert(IsIntegerArithmeticSafe<type, Lhs, Rhs>::value, ""); - static const bool is_contained = true; -}; - -template <typename Lhs, typename Rhs> -struct FastIntegerArithmeticPromotion<Lhs, Rhs, false> { - using type = typename BigEnoughPromotion<Lhs, Rhs>::type; - static const bool is_contained = false; -}; - -// Extracts the underlying type from an enum. -template <typename T, bool is_enum = std::is_enum<T>::value> -struct ArithmeticOrUnderlyingEnum; - -template <typename T> -struct ArithmeticOrUnderlyingEnum<T, true> { - using type = typename std::underlying_type<T>::type; - static const bool value = std::is_arithmetic<type>::value; -}; - -template <typename T> -struct ArithmeticOrUnderlyingEnum<T, false> { - using type = T; - static const bool value = std::is_arithmetic<type>::value; -}; - -// The following are helper templates used in the CheckedNumeric class. -template <typename T> -class CheckedNumeric; - -template <typename T> -class ClampedNumeric; - -template <typename T> -class StrictNumeric; - -// Used to treat CheckedNumeric and arithmetic underlying types the same. -template <typename T> -struct UnderlyingType { - using type = typename ArithmeticOrUnderlyingEnum<T>::type; - static const bool is_numeric = std::is_arithmetic<type>::value; - static const bool is_checked = false; - static const bool is_clamped = false; - static const bool is_strict = false; -}; - -template <typename T> -struct UnderlyingType<CheckedNumeric<T>> { - using type = T; - static const bool is_numeric = true; - static const bool is_checked = true; - static const bool is_clamped = false; - static const bool is_strict = false; -}; - -template <typename T> -struct UnderlyingType<ClampedNumeric<T>> { - using type = T; - static const bool is_numeric = true; - static const bool is_checked = false; - static const bool is_clamped = true; - static const bool is_strict = false; -}; - -template <typename T> -struct UnderlyingType<StrictNumeric<T>> { - using type = T; - static const bool is_numeric = true; - static const bool is_checked = false; - static const bool is_clamped = false; - static const bool is_strict = true; -}; - -template <typename L, typename R> -struct IsCheckedOp { - static const bool value = - UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric && - (UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked); -}; - -template <typename L, typename R> -struct IsClampedOp { - static const bool value = - UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric && - (UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped) && - !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked); -}; - -template <typename L, typename R> -struct IsStrictOp { - static const bool value = - UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric && - (UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict) && - !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked) && - !(UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped); -}; - -// as_signed<> returns the supplied integral value (or integral castable -// Numeric template) cast as a signed integral of equivalent precision. -// I.e. it's mostly an alias for: static_cast<std::make_signed<T>::type>(t) -template <typename Src> -constexpr typename std::make_signed< - typename base::internal::UnderlyingType<Src>::type>::type -as_signed(const Src value) { - static_assert(std::is_integral<decltype(as_signed(value))>::value, - "Argument must be a signed or unsigned integer type."); - return static_cast<decltype(as_signed(value))>(value); -} - -// as_unsigned<> returns the supplied integral value (or integral castable -// Numeric template) cast as an unsigned integral of equivalent precision. -// I.e. it's mostly an alias for: static_cast<std::make_unsigned<T>::type>(t) -template <typename Src> -constexpr typename std::make_unsigned< - typename base::internal::UnderlyingType<Src>::type>::type -as_unsigned(const Src value) { - static_assert(std::is_integral<decltype(as_unsigned(value))>::value, - "Argument must be a signed or unsigned integer type."); - return static_cast<decltype(as_unsigned(value))>(value); -} - -template <typename L, typename R> -constexpr bool IsLessImpl(const L lhs, - const R rhs, - const RangeCheck l_range, - const RangeCheck r_range) { - return l_range.IsUnderflow() || r_range.IsOverflow() || - (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) < - static_cast<decltype(lhs + rhs)>(rhs)); -} - -template <typename L, typename R> -struct IsLess { - static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, - "Types must be numeric."); - static constexpr bool Test(const L lhs, const R rhs) { - return IsLessImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), - DstRangeRelationToSrcRange<L>(rhs)); - } -}; - -template <typename L, typename R> -constexpr bool IsLessOrEqualImpl(const L lhs, - const R rhs, - const RangeCheck l_range, - const RangeCheck r_range) { - return l_range.IsUnderflow() || r_range.IsOverflow() || - (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) <= - static_cast<decltype(lhs + rhs)>(rhs)); -} - -template <typename L, typename R> -struct IsLessOrEqual { - static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, - "Types must be numeric."); - static constexpr bool Test(const L lhs, const R rhs) { - return IsLessOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), - DstRangeRelationToSrcRange<L>(rhs)); - } -}; - -template <typename L, typename R> -constexpr bool IsGreaterImpl(const L lhs, - const R rhs, - const RangeCheck l_range, - const RangeCheck r_range) { - return l_range.IsOverflow() || r_range.IsUnderflow() || - (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) > - static_cast<decltype(lhs + rhs)>(rhs)); -} - -template <typename L, typename R> -struct IsGreater { - static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, - "Types must be numeric."); - static constexpr bool Test(const L lhs, const R rhs) { - return IsGreaterImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), - DstRangeRelationToSrcRange<L>(rhs)); - } -}; - -template <typename L, typename R> -constexpr bool IsGreaterOrEqualImpl(const L lhs, - const R rhs, - const RangeCheck l_range, - const RangeCheck r_range) { - return l_range.IsOverflow() || r_range.IsUnderflow() || - (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) >= - static_cast<decltype(lhs + rhs)>(rhs)); -} - -template <typename L, typename R> -struct IsGreaterOrEqual { - static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, - "Types must be numeric."); - static constexpr bool Test(const L lhs, const R rhs) { - return IsGreaterOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), - DstRangeRelationToSrcRange<L>(rhs)); - } -}; - -template <typename L, typename R> -struct IsEqual { - static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, - "Types must be numeric."); - static constexpr bool Test(const L lhs, const R rhs) { - return DstRangeRelationToSrcRange<R>(lhs) == - DstRangeRelationToSrcRange<L>(rhs) && - static_cast<decltype(lhs + rhs)>(lhs) == - static_cast<decltype(lhs + rhs)>(rhs); - } -}; - -template <typename L, typename R> -struct IsNotEqual { - static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, - "Types must be numeric."); - static constexpr bool Test(const L lhs, const R rhs) { - return DstRangeRelationToSrcRange<R>(lhs) != - DstRangeRelationToSrcRange<L>(rhs) || - static_cast<decltype(lhs + rhs)>(lhs) != - static_cast<decltype(lhs + rhs)>(rhs); - } -}; - -// These perform the actual math operations on the CheckedNumerics. -// Binary arithmetic operations. -template <template <typename, typename> class C, typename L, typename R> -constexpr bool SafeCompare(const L lhs, const R rhs) { - static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, - "Types must be numeric."); - using Promotion = BigEnoughPromotion<L, R>; - using BigType = typename Promotion::type; - return Promotion::is_contained - // Force to a larger type for speed if both are contained. - ? C<BigType, BigType>::Test( - static_cast<BigType>(static_cast<L>(lhs)), - static_cast<BigType>(static_cast<R>(rhs))) - // Let the template functions figure it out for mixed types. - : C<L, R>::Test(lhs, rhs); -} - -template <typename Dst, typename Src> -constexpr bool IsMaxInRangeForNumericType() { - return IsGreaterOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::max(), - std::numeric_limits<Src>::max()); -} - -template <typename Dst, typename Src> -constexpr bool IsMinInRangeForNumericType() { - return IsLessOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::lowest(), - std::numeric_limits<Src>::lowest()); -} - -template <typename Dst, typename Src> -constexpr Dst CommonMax() { - return !IsMaxInRangeForNumericType<Dst, Src>() - ? Dst(std::numeric_limits<Dst>::max()) - : Dst(std::numeric_limits<Src>::max()); -} - -template <typename Dst, typename Src> -constexpr Dst CommonMin() { - return !IsMinInRangeForNumericType<Dst, Src>() - ? Dst(std::numeric_limits<Dst>::lowest()) - : Dst(std::numeric_limits<Src>::lowest()); -} - -// This is a wrapper to generate return the max or min for a supplied type. -// If the argument is false, the returned value is the maximum. If true the -// returned value is the minimum. -template <typename Dst, typename Src = Dst> -constexpr Dst CommonMaxOrMin(bool is_min) { - return is_min ? CommonMin<Dst, Src>() : CommonMax<Dst, Src>(); -} - -} // namespace partition_alloc::internal::base::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/numerics/safe_math.h b/base/allocator/partition_allocator/partition_alloc_base/numerics/safe_math.h deleted file mode 100644 index dfb59ca..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/numerics/safe_math.h +++ /dev/null
@@ -1,12 +0,0 @@ -// Copyright 2017 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_SAFE_MATH_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_SAFE_MATH_H_ - -#include "base/allocator/partition_allocator/partition_alloc_base/numerics/checked_math.h" -#include "base/allocator/partition_allocator/partition_alloc_base/numerics/clamped_math.h" -#include "base/allocator/partition_allocator/partition_alloc_base/numerics/safe_conversions.h" - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_SAFE_MATH_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/numerics/safe_math_arm_impl.h b/base/allocator/partition_allocator/partition_alloc_base/numerics/safe_math_arm_impl.h deleted file mode 100644 index c284e3e..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/numerics/safe_math_arm_impl.h +++ /dev/null
@@ -1,123 +0,0 @@ -// Copyright 2017 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_SAFE_MATH_ARM_IMPL_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_SAFE_MATH_ARM_IMPL_H_ - -#include <cassert> -#include <type_traits> - -#include "base/allocator/partition_allocator/partition_alloc_base/numerics/safe_conversions.h" - -namespace partition_alloc::internal::base::internal { - -template <typename T, typename U> -struct CheckedMulFastAsmOp { - static const bool is_supported = - kEnableAsmCode && FastIntegerArithmeticPromotion<T, U>::is_contained; - - // The following is not an assembler routine and is thus constexpr safe, it - // just emits much more efficient code than the Clang and GCC builtins for - // performing overflow-checked multiplication when a twice wider type is - // available. The below compiles down to 2-3 instructions, depending on the - // width of the types in use. - // As an example, an int32_t multiply compiles to: - // smull r0, r1, r0, r1 - // cmp r1, r1, asr #31 - // And an int16_t multiply compiles to: - // smulbb r1, r1, r0 - // asr r2, r1, #16 - // cmp r2, r1, asr #15 - template <typename V> - static constexpr bool Do(T x, U y, V* result) { - using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type; - Promotion presult; - - presult = static_cast<Promotion>(x) * static_cast<Promotion>(y); - if (!IsValueInRangeForNumericType<V>(presult)) - return false; - *result = static_cast<V>(presult); - return true; - } -}; - -template <typename T, typename U> -struct ClampedAddFastAsmOp { - static const bool is_supported = - kEnableAsmCode && BigEnoughPromotion<T, U>::is_contained && - IsTypeInRangeForNumericType< - int32_t, - typename BigEnoughPromotion<T, U>::type>::value; - - template <typename V> - __attribute__((always_inline)) static V Do(T x, U y) { - // This will get promoted to an int, so let the compiler do whatever is - // clever and rely on the saturated cast to bounds check. - if (IsIntegerArithmeticSafe<int, T, U>::value) - return saturated_cast<V>(x + y); - - int32_t result; - int32_t x_i32 = checked_cast<int32_t>(x); - int32_t y_i32 = checked_cast<int32_t>(y); - - asm("qadd %[result], %[first], %[second]" - : [result] "=r"(result) - : [first] "r"(x_i32), [second] "r"(y_i32)); - return saturated_cast<V>(result); - } -}; - -template <typename T, typename U> -struct ClampedSubFastAsmOp { - static const bool is_supported = - kEnableAsmCode && BigEnoughPromotion<T, U>::is_contained && - IsTypeInRangeForNumericType< - int32_t, - typename BigEnoughPromotion<T, U>::type>::value; - - template <typename V> - __attribute__((always_inline)) static V Do(T x, U y) { - // This will get promoted to an int, so let the compiler do whatever is - // clever and rely on the saturated cast to bounds check. - if (IsIntegerArithmeticSafe<int, T, U>::value) - return saturated_cast<V>(x - y); - - int32_t result; - int32_t x_i32 = checked_cast<int32_t>(x); - int32_t y_i32 = checked_cast<int32_t>(y); - - asm("qsub %[result], %[first], %[second]" - : [result] "=r"(result) - : [first] "r"(x_i32), [second] "r"(y_i32)); - return saturated_cast<V>(result); - } -}; - -template <typename T, typename U> -struct ClampedMulFastAsmOp { - static const bool is_supported = - kEnableAsmCode && CheckedMulFastAsmOp<T, U>::is_supported; - - template <typename V> - __attribute__((always_inline)) static V Do(T x, U y) { - // Use the CheckedMulFastAsmOp for full-width 32-bit values, because - // it's fewer instructions than promoting and then saturating. - if (!IsIntegerArithmeticSafe<int32_t, T, U>::value && - !IsIntegerArithmeticSafe<uint32_t, T, U>::value) { - V result; - return CheckedMulFastAsmOp<T, U>::Do(x, y, &result) - ? result - : CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y)); - } - - assert((FastIntegerArithmeticPromotion<T, U>::is_contained)); - using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type; - return saturated_cast<V>(static_cast<Promotion>(x) * - static_cast<Promotion>(y)); - } -}; - -} // namespace partition_alloc::internal::base::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_SAFE_MATH_ARM_IMPL_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/numerics/safe_math_clang_gcc_impl.h b/base/allocator/partition_allocator/partition_alloc_base/numerics/safe_math_clang_gcc_impl.h deleted file mode 100644 index 956a892..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/numerics/safe_math_clang_gcc_impl.h +++ /dev/null
@@ -1,155 +0,0 @@ -// Copyright 2017 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_SAFE_MATH_CLANG_GCC_IMPL_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_SAFE_MATH_CLANG_GCC_IMPL_H_ - -#include <cassert> -#include <limits> -#include <type_traits> - -#include "base/allocator/partition_allocator/partition_alloc_base/numerics/safe_conversions.h" - -#if !defined(__native_client__) && (defined(__ARMEL__) || defined(__arch64__)) -#include "base/allocator/partition_allocator/partition_alloc_base/numerics/safe_math_arm_impl.h" -#define PA_BASE_HAS_ASSEMBLER_SAFE_MATH (1) -#else -#define PA_BASE_HAS_ASSEMBLER_SAFE_MATH (0) -#endif - -namespace partition_alloc::internal::base::internal { - -// These are the non-functioning boilerplate implementations of the optimized -// safe math routines. -#if !PA_BASE_HAS_ASSEMBLER_SAFE_MATH -template <typename T, typename U> -struct CheckedMulFastAsmOp { - static const bool is_supported = false; - template <typename V> - static constexpr bool Do(T, U, V*) { - // Force a compile failure if instantiated. - return CheckOnFailure::template HandleFailure<bool>(); - } -}; - -template <typename T, typename U> -struct ClampedAddFastAsmOp { - static const bool is_supported = false; - template <typename V> - static constexpr V Do(T, U) { - // Force a compile failure if instantiated. - return CheckOnFailure::template HandleFailure<V>(); - } -}; - -template <typename T, typename U> -struct ClampedSubFastAsmOp { - static const bool is_supported = false; - template <typename V> - static constexpr V Do(T, U) { - // Force a compile failure if instantiated. - return CheckOnFailure::template HandleFailure<V>(); - } -}; - -template <typename T, typename U> -struct ClampedMulFastAsmOp { - static const bool is_supported = false; - template <typename V> - static constexpr V Do(T, U) { - // Force a compile failure if instantiated. - return CheckOnFailure::template HandleFailure<V>(); - } -}; -#endif // PA_BASE_HAS_ASSEMBLER_SAFE_MATH -#undef PA_BASE_HAS_ASSEMBLER_SAFE_MATH - -template <typename T, typename U> -struct CheckedAddFastOp { - static const bool is_supported = true; - template <typename V> - __attribute__((always_inline)) static constexpr bool Do(T x, U y, V* result) { - return !__builtin_add_overflow(x, y, result); - } -}; - -template <typename T, typename U> -struct CheckedSubFastOp { - static const bool is_supported = true; - template <typename V> - __attribute__((always_inline)) static constexpr bool Do(T x, U y, V* result) { - return !__builtin_sub_overflow(x, y, result); - } -}; - -template <typename T, typename U> -struct CheckedMulFastOp { -#if defined(__clang__) - // TODO(jschuh): Get the Clang runtime library issues sorted out so we can - // support full-width, mixed-sign multiply builtins. - // https://crbug.com/613003 - // We can support intptr_t, uintptr_t, or a smaller common type. - static const bool is_supported = - (IsTypeInRangeForNumericType<intptr_t, T>::value && - IsTypeInRangeForNumericType<intptr_t, U>::value) || - (IsTypeInRangeForNumericType<uintptr_t, T>::value && - IsTypeInRangeForNumericType<uintptr_t, U>::value); -#else - static const bool is_supported = true; -#endif - template <typename V> - __attribute__((always_inline)) static constexpr bool Do(T x, U y, V* result) { - return CheckedMulFastAsmOp<T, U>::is_supported - ? CheckedMulFastAsmOp<T, U>::Do(x, y, result) - : !__builtin_mul_overflow(x, y, result); - } -}; - -template <typename T, typename U> -struct ClampedAddFastOp { - static const bool is_supported = ClampedAddFastAsmOp<T, U>::is_supported; - template <typename V> - __attribute__((always_inline)) static V Do(T x, U y) { - return ClampedAddFastAsmOp<T, U>::template Do<V>(x, y); - } -}; - -template <typename T, typename U> -struct ClampedSubFastOp { - static const bool is_supported = ClampedSubFastAsmOp<T, U>::is_supported; - template <typename V> - __attribute__((always_inline)) static V Do(T x, U y) { - return ClampedSubFastAsmOp<T, U>::template Do<V>(x, y); - } -}; - -template <typename T, typename U> -struct ClampedMulFastOp { - static const bool is_supported = ClampedMulFastAsmOp<T, U>::is_supported; - template <typename V> - __attribute__((always_inline)) static V Do(T x, U y) { - return ClampedMulFastAsmOp<T, U>::template Do<V>(x, y); - } -}; - -template <typename T> -struct ClampedNegFastOp { - static const bool is_supported = std::is_signed<T>::value; - __attribute__((always_inline)) static T Do(T value) { - // Use this when there is no assembler path available. - if (!ClampedSubFastAsmOp<T, T>::is_supported) { - T result; - return !__builtin_sub_overflow(T(0), value, &result) - ? result - : std::numeric_limits<T>::max(); - } - - // Fallback to the normal subtraction path. - return ClampedSubFastOp<T, T>::template Do<T>(T(0), value); - } -}; - -} // namespace partition_alloc::internal::base::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_SAFE_MATH_CLANG_GCC_IMPL_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/numerics/safe_math_shared_impl.h b/base/allocator/partition_allocator/partition_alloc_base/numerics/safe_math_shared_impl.h deleted file mode 100644 index e644a67..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/numerics/safe_math_shared_impl.h +++ /dev/null
@@ -1,215 +0,0 @@ -// Copyright 2017 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_ - -#include <stddef.h> -#include <stdint.h> - -#include <cassert> -#include <climits> -#include <cmath> -#include <cstdlib> -#include <limits> -#include <type_traits> - -#include "base/allocator/partition_allocator/partition_alloc_base/numerics/safe_conversions.h" -#include "build/build_config.h" - -#if BUILDFLAG(IS_ASMJS) -// Optimized safe math instructions are incompatible with asmjs. -#define PA_BASE_HAS_OPTIMIZED_SAFE_MATH (0) -// Where available use builtin math overflow support on Clang and GCC. -#elif !defined(__native_client__) && \ - ((defined(__clang__) && \ - ((__clang_major__ > 3) || \ - (__clang_major__ == 3 && __clang_minor__ >= 4))) || \ - (defined(__GNUC__) && __GNUC__ >= 5)) -#include "base/allocator/partition_allocator/partition_alloc_base/numerics/safe_math_clang_gcc_impl.h" -#define PA_BASE_HAS_OPTIMIZED_SAFE_MATH (1) -#else -#define PA_BASE_HAS_OPTIMIZED_SAFE_MATH (0) -#endif - -namespace partition_alloc::internal::base::internal { - -// These are the non-functioning boilerplate implementations of the optimized -// safe math routines. -#if !PA_BASE_HAS_OPTIMIZED_SAFE_MATH -template <typename T, typename U> -struct CheckedAddFastOp { - static const bool is_supported = false; - template <typename V> - static constexpr bool Do(T, U, V*) { - // Force a compile failure if instantiated. - return CheckOnFailure::template HandleFailure<bool>(); - } -}; - -template <typename T, typename U> -struct CheckedSubFastOp { - static const bool is_supported = false; - template <typename V> - static constexpr bool Do(T, U, V*) { - // Force a compile failure if instantiated. - return CheckOnFailure::template HandleFailure<bool>(); - } -}; - -template <typename T, typename U> -struct CheckedMulFastOp { - static const bool is_supported = false; - template <typename V> - static constexpr bool Do(T, U, V*) { - // Force a compile failure if instantiated. - return CheckOnFailure::template HandleFailure<bool>(); - } -}; - -template <typename T, typename U> -struct ClampedAddFastOp { - static const bool is_supported = false; - template <typename V> - static constexpr V Do(T, U) { - // Force a compile failure if instantiated. - return CheckOnFailure::template HandleFailure<V>(); - } -}; - -template <typename T, typename U> -struct ClampedSubFastOp { - static const bool is_supported = false; - template <typename V> - static constexpr V Do(T, U) { - // Force a compile failure if instantiated. - return CheckOnFailure::template HandleFailure<V>(); - } -}; - -template <typename T, typename U> -struct ClampedMulFastOp { - static const bool is_supported = false; - template <typename V> - static constexpr V Do(T, U) { - // Force a compile failure if instantiated. - return CheckOnFailure::template HandleFailure<V>(); - } -}; - -template <typename T> -struct ClampedNegFastOp { - static const bool is_supported = false; - static constexpr T Do(T) { - // Force a compile failure if instantiated. - return CheckOnFailure::template HandleFailure<T>(); - } -}; -#endif // PA_BASE_HAS_OPTIMIZED_SAFE_MATH -#undef PA_BASE_HAS_OPTIMIZED_SAFE_MATH - -// This is used for UnsignedAbs, where we need to support floating-point -// template instantiations even though we don't actually support the operations. -// However, there is no corresponding implementation of e.g. SafeUnsignedAbs, -// so the float versions will not compile. -template <typename Numeric, - bool IsInteger = std::is_integral<Numeric>::value, - bool IsFloat = std::is_floating_point<Numeric>::value> -struct UnsignedOrFloatForSize; - -template <typename Numeric> -struct UnsignedOrFloatForSize<Numeric, true, false> { - using type = typename std::make_unsigned<Numeric>::type; -}; - -template <typename Numeric> -struct UnsignedOrFloatForSize<Numeric, false, true> { - using type = Numeric; -}; - -// Wrap the unary operations to allow SFINAE when instantiating integrals versus -// floating points. These don't perform any overflow checking. Rather, they -// exhibit well-defined overflow semantics and rely on the caller to detect -// if an overflow occurred. - -template <typename T, - typename std::enable_if<std::is_integral<T>::value>::type* = nullptr> -constexpr T NegateWrapper(T value) { - using UnsignedT = typename std::make_unsigned<T>::type; - // This will compile to a NEG on Intel, and is normal negation on ARM. - return static_cast<T>(UnsignedT(0) - static_cast<UnsignedT>(value)); -} - -template < - typename T, - typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr> -constexpr T NegateWrapper(T value) { - return -value; -} - -template <typename T, - typename std::enable_if<std::is_integral<T>::value>::type* = nullptr> -constexpr typename std::make_unsigned<T>::type InvertWrapper(T value) { - return ~value; -} - -template <typename T, - typename std::enable_if<std::is_integral<T>::value>::type* = nullptr> -constexpr T AbsWrapper(T value) { - return static_cast<T>(SafeUnsignedAbs(value)); -} - -template < - typename T, - typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr> -constexpr T AbsWrapper(T value) { - return value < 0 ? -value : value; -} - -template <template <typename, typename, typename> class M, - typename L, - typename R> -struct MathWrapper { - using math = M<typename UnderlyingType<L>::type, - typename UnderlyingType<R>::type, - void>; - using type = typename math::result_type; -}; - -// The following macros are just boilerplate for the standard arithmetic -// operator overloads and variadic function templates. A macro isn't the nicest -// solution, but it beats rewriting these over and over again. -#define PA_BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME) \ - template <typename L, typename R, typename... Args> \ - constexpr auto CL_ABBR##OP_NAME(const L lhs, const R rhs, \ - const Args... args) { \ - return CL_ABBR##MathOp<CLASS##OP_NAME##Op, L, R, Args...>(lhs, rhs, \ - args...); \ - } - -#define PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(CLASS, CL_ABBR, OP_NAME, OP, \ - CMP_OP) \ - /* Binary arithmetic operator for all CLASS##Numeric operations. */ \ - template <typename L, typename R, \ - typename std::enable_if<Is##CLASS##Op<L, R>::value>::type* = \ - nullptr> \ - constexpr CLASS##Numeric< \ - typename MathWrapper<CLASS##OP_NAME##Op, L, R>::type> \ - operator OP(const L lhs, const R rhs) { \ - return decltype(lhs OP rhs)::template MathOp<CLASS##OP_NAME##Op>(lhs, \ - rhs); \ - } \ - /* Assignment arithmetic operator implementation from CLASS##Numeric. */ \ - template <typename L> \ - template <typename R> \ - constexpr CLASS##Numeric<L>& CLASS##Numeric<L>::operator CMP_OP( \ - const R rhs) { \ - return MathOp<CLASS##OP_NAME##Op>(rhs); \ - } \ - /* Variadic arithmetic functions that return CLASS##Numeric. */ \ - PA_BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME) - -} // namespace partition_alloc::internal::base::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/posix/eintr_wrapper.h b/base/allocator/partition_allocator/partition_alloc_base/posix/eintr_wrapper.h deleted file mode 100644 index ea55ee6..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/posix/eintr_wrapper.h +++ /dev/null
@@ -1,58 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This provides a wrapper around system calls which may be interrupted by a -// signal and return EINTR. See man 7 signal. -// To prevent long-lasting loops (which would likely be a bug, such as a signal -// that should be masked) to go unnoticed, there is a limit after which the -// caller will nonetheless see an EINTR in Debug builds. -// -// On Windows and Fuchsia, this wrapper macro does nothing because there are no -// signals. -// -// Don't wrap close calls in HANDLE_EINTR. Use IGNORE_EINTR if the return -// value of close is significant. See http://crbug.com/269623. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_POSIX_EINTR_WRAPPER_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_POSIX_EINTR_WRAPPER_H_ - -#include "build/build_config.h" - -#if BUILDFLAG(IS_POSIX) - -#include <errno.h> - -#if defined(NDEBUG) - -#define PA_HANDLE_EINTR(x) \ - ({ \ - decltype(x) eintr_wrapper_result; \ - do { \ - eintr_wrapper_result = (x); \ - } while (eintr_wrapper_result == -1 && errno == EINTR); \ - eintr_wrapper_result; \ - }) - -#else - -#define PA_HANDLE_EINTR(x) \ - ({ \ - int eintr_wrapper_counter = 0; \ - decltype(x) eintr_wrapper_result; \ - do { \ - eintr_wrapper_result = (x); \ - } while (eintr_wrapper_result == -1 && errno == EINTR && \ - eintr_wrapper_counter++ < 100); \ - eintr_wrapper_result; \ - }) - -#endif // NDEBUG - -#else // !BUILDFLAG(IS_POSIX) - -#define PA_HANDLE_EINTR(x) (x) - -#endif // !BUILDFLAG(IS_POSIX) - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_POSIX_EINTR_WRAPPER_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/posix/safe_strerror.cc b/base/allocator/partition_allocator/partition_alloc_base/posix/safe_strerror.cc deleted file mode 100644 index 5c4de6e..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/posix/safe_strerror.cc +++ /dev/null
@@ -1,113 +0,0 @@ -// Copyright 2006-2009 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/posix/safe_strerror.h" - -#include <errno.h> -#include <stdio.h> -#include <string.h> - -#include "build/build_config.h" - -namespace partition_alloc::internal::base { - -#if defined(__GLIBC__) -#define USE_HISTORICAL_STRERROR_R 1 -// Post-L versions of bionic define the GNU-specific strerror_r if _GNU_SOURCE -// is defined, but the symbol is renamed to __gnu_strerror_r which only exists -// on those later versions. For parity, add the same condition as bionic. -#elif defined(__BIONIC__) && defined(_GNU_SOURCE) && __ANDROID_API__ >= 23 -#define USE_HISTORICAL_STRERROR_R 1 -#else -#define USE_HISTORICAL_STRERROR_R 0 -#endif - -#if USE_HISTORICAL_STRERROR_R -// glibc has two strerror_r functions: a historical GNU-specific one that -// returns type char *, and a POSIX.1-2001 compliant one available since 2.3.4 -// that returns int. This wraps the GNU-specific one. -[[maybe_unused]] static void wrap_posix_strerror_r( - char* (*strerror_r_ptr)(int, char*, size_t), - int err, - char* buf, - size_t len) { - // GNU version. - char* rc = (*strerror_r_ptr)(err, buf, len); - if (rc != buf) { - // glibc did not use buf and returned a static string instead. Copy it - // into buf. - buf[0] = '\0'; - strncat(buf, rc, len - 1); - } - // The GNU version never fails. Unknown errors get an "unknown error" message. - // The result is always null terminated. -} -#endif // USE_HISTORICAL_STRERROR_R - -// Wrapper for strerror_r functions that implement the POSIX interface. POSIX -// does not define the behaviour for some of the edge cases, so we wrap it to -// guarantee that they are handled. This is compiled on all POSIX platforms, but -// it will only be used on Linux if the POSIX strerror_r implementation is -// being used (see below). -[[maybe_unused]] static void wrap_posix_strerror_r( - int (*strerror_r_ptr)(int, char*, size_t), - int err, - char* buf, - size_t len) { - int old_errno = errno; - // Have to cast since otherwise we get an error if this is the GNU version - // (but in such a scenario this function is never called). Sadly we can't use - // C++-style casts because the appropriate one is reinterpret_cast but it's - // considered illegal to reinterpret_cast a type to itself, so we get an - // error in the opposite case. - int result = (*strerror_r_ptr)(err, buf, len); - if (result == 0) { - // POSIX is vague about whether the string will be terminated, although - // it indirectly implies that typically ERANGE will be returned, instead - // of truncating the string. We play it safe by always terminating the - // string explicitly. - buf[len - 1] = '\0'; - } else { - // Error. POSIX is vague about whether the return value is itself a system - // error code or something else. On Linux currently it is -1 and errno is - // set. On BSD-derived systems it is a system error and errno is unchanged. - // We try and detect which case it is so as to put as much useful info as - // we can into our message. - int strerror_error; // The error encountered in strerror - int new_errno = errno; - if (new_errno != old_errno) { - // errno was changed, so probably the return value is just -1 or something - // else that doesn't provide any info, and errno is the error. - strerror_error = new_errno; - } else { - // Either the error from strerror_r was the same as the previous value, or - // errno wasn't used. Assume the latter. - strerror_error = result; - } - // snprintf truncates and always null-terminates. - snprintf(buf, len, "Error %d while retrieving error %d", strerror_error, - err); - } - errno = old_errno; -} - -void safe_strerror_r(int err, char* buf, size_t len) { - if (buf == nullptr || len <= 0) { - return; - } - // If using glibc (i.e., Linux), the compiler will automatically select the - // appropriate overloaded function based on the function type of strerror_r. - // The other one will be elided from the translation unit since both are - // static. - wrap_posix_strerror_r(&strerror_r, err, buf, len); -} - -std::string safe_strerror(int err) { - const int buffer_size = 256; - char buf[buffer_size]; - safe_strerror_r(err, buf, sizeof(buf)); - return std::string(buf); -} - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/posix/safe_strerror.h b/base/allocator/partition_allocator/partition_alloc_base/posix/safe_strerror.h deleted file mode 100644 index fe402ed..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/posix/safe_strerror.h +++ /dev/null
@@ -1,45 +0,0 @@ -// Copyright 2011 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_POSIX_SAFE_STRERROR_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_POSIX_SAFE_STRERROR_H_ - -#include <stddef.h> - -#include <string> - -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" - -namespace partition_alloc::internal::base { - -// BEFORE using anything from this file, first look at PLOG and friends in -// logging.h and use them instead if applicable. -// -// This file declares safe, portable alternatives to the POSIX strerror() -// function. strerror() is inherently unsafe in multi-threaded apps and should -// never be used. Doing so can cause crashes. Additionally, the thread-safe -// alternative strerror_r varies in semantics across platforms. Use these -// functions instead. - -// Thread-safe strerror function with dependable semantics that never fails. -// It will write the string form of error "err" to buffer buf of length len. -// If there is an error calling the OS's strerror_r() function then a message to -// that effect will be printed into buf, truncating if necessary. The final -// result is always null-terminated. The value of errno is never changed. -// -// Use this instead of strerror_r(). -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -void safe_strerror_r(int err, char* buf, size_t len); - -// Calls safe_strerror_r with a buffer of suitable size and returns the result -// in a C++ string. -// -// Use this instead of strerror(). Note though that safe_strerror_r will be -// more robust in the case of heap corruption errors, since it doesn't need to -// allocate a string. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) std::string safe_strerror(int err); - -} // namespace partition_alloc::internal::base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_POSIX_SAFE_STRERROR_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/rand_util.cc b/base/allocator/partition_allocator/partition_alloc_base/rand_util.cc deleted file mode 100644 index 1088e4e..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/rand_util.cc +++ /dev/null
@@ -1,74 +0,0 @@ -// Copyright 2011 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/rand_util.h" - -#include <limits.h> -#include <math.h> -#include <stdint.h> - -#include <limits> - -#include "base/allocator/partition_allocator/partition_alloc_check.h" - -namespace partition_alloc::internal::base { - -uint64_t RandUint64() { - uint64_t number; - RandBytes(&number, sizeof(number)); - return number; -} - -uint64_t RandGenerator(uint64_t range) { - PA_DCHECK(range > 0u); - // We must discard random results above this number, as they would - // make the random generator non-uniform (consider e.g. if - // MAX_UINT64 was 7 and |range| was 5, then a result of 1 would be twice - // as likely as a result of 3 or 4). - uint64_t max_acceptable_value = - (std::numeric_limits<uint64_t>::max() / range) * range - 1; - - uint64_t value; - do { - value = base::RandUint64(); - } while (value > max_acceptable_value); - - return value % range; -} - -InsecureRandomGenerator::InsecureRandomGenerator() { - a_ = base::RandUint64(); - b_ = base::RandUint64(); -} - -void InsecureRandomGenerator::ReseedForTesting(uint64_t seed) { - a_ = seed; - b_ = seed; -} - -uint64_t InsecureRandomGenerator::RandUint64() { - // Using XorShift128+, which is simple and widely used. See - // https://en.wikipedia.org/wiki/Xorshift#xorshift+ for details. - uint64_t t = a_; - const uint64_t s = b_; - - a_ = s; - t ^= t << 23; - t ^= t >> 17; - t ^= s ^ (s >> 26); - b_ = t; - - return t + s; -} - -uint32_t InsecureRandomGenerator::RandUint32() { - // The generator usually returns an uint64_t, truncate it. - // - // It is noted in this paper (https://arxiv.org/abs/1810.05313) that the - // lowest 32 bits fail some statistical tests from the Big Crush - // suite. Use the higher ones instead. - return this->RandUint64() >> 32; -} - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/rand_util.h b/base/allocator/partition_allocator/partition_alloc_base/rand_util.h deleted file mode 100644 index 3dc55df..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/rand_util.h +++ /dev/null
@@ -1,95 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_RAND_UTIL_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_RAND_UTIL_H_ - -#include <stddef.h> -#include <stdint.h> - -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_base/gtest_prod_util.h" -#include "build/build_config.h" - -namespace partition_alloc { -class RandomGenerator; -} // namespace partition_alloc - -namespace partition_alloc::internal::base { - -// Returns a random number in range [0, UINT64_MAX]. Thread-safe. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) uint64_t RandUint64(); - -// Returns a random number in range [0, range). Thread-safe. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) uint64_t RandGenerator(uint64_t range); - -// Fills |output_length| bytes of |output| with random data. Thread-safe. -// -// Although implementations are required to use a cryptographically secure -// random number source, code outside of base/ that relies on this should use -// crypto::RandBytes instead to ensure the requirement is easily discoverable. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -void RandBytes(void* output, size_t output_length); - -// Fast, insecure pseudo-random number generator. -// -// WARNING: This is not the generator you are looking for. This has significant -// caveats: -// - It is non-cryptographic, so easy to miuse -// - It is neither fork() nor clone()-safe. -// - Synchronization is up to the client. -// -// Always prefer base::Rand*() above, unless you have a use case where its -// overhead is too high, or system calls are disallowed. -// -// Performance: As of 2021, rough overhead on Linux on a desktop machine of -// base::RandUint64() is ~800ns per call (it performs a system call). On Windows -// it is lower. On the same machine, this generator's cost is ~2ns per call, -// regardless of platform. -// -// This is different from |Rand*()| above as it is guaranteed to never make a -// system call to generate a new number, except to seed it. This should *never* -// be used for cryptographic applications, and is not thread-safe. -// -// It is seeded using base::RandUint64() in the constructor, meaning that it -// doesn't need to be seeded. It can be re-seeded though, with -// ReseedForTesting(). Its period is long enough that it should not need to be -// re-seeded during use. -// -// Uses the XorShift128+ generator under the hood. -class PA_COMPONENT_EXPORT(PARTITION_ALLOC) InsecureRandomGenerator { - public: - // Never use outside testing, not enough entropy. - void ReseedForTesting(uint64_t seed); - - uint32_t RandUint32(); - uint64_t RandUint64(); - - private: - InsecureRandomGenerator(); - // State. - uint64_t a_ = 0, b_ = 0; - - // Before adding a new friend class, make sure that the overhead of - // base::Rand*() is too high, using something more representative than a - // microbenchmark. - // - // PartitionAlloc allocations should not take more than 40-50ns per - // malloc()/free() pair, otherwise high-level benchmarks regress, and does not - // need a secure PRNG, as it's used for ASLR and zeroing some allocations at - // free() time. - friend class ::partition_alloc::RandomGenerator; - - PA_FRIEND_TEST_ALL_PREFIXES( - PartitionAllocBaseRandUtilTest, - InsecureRandomGeneratorProducesBothValuesOfAllBits); - PA_FRIEND_TEST_ALL_PREFIXES(PartitionAllocBaseRandUtilTest, - InsecureRandomGeneratorChiSquared); - PA_FRIEND_TEST_ALL_PREFIXES(PartitionAllocBaseRandUtilTest, - InsecureRandomGeneratorRandDouble); -}; - -} // namespace partition_alloc::internal::base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_RAND_UTIL_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/rand_util_fuchsia.cc b/base/allocator/partition_allocator/partition_alloc_base/rand_util_fuchsia.cc deleted file mode 100644 index 8839f8d..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/rand_util_fuchsia.cc +++ /dev/null
@@ -1,15 +0,0 @@ -// Copyright 2017 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/rand_util.h" - -#include <zircon/syscalls.h> - -namespace partition_alloc::internal::base { - -void RandBytes(void* output, size_t output_length) { - zx_cprng_draw(output, output_length); -} - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/rand_util_pa_unittest.cc b/base/allocator/partition_allocator/partition_alloc_base/rand_util_pa_unittest.cc deleted file mode 100644 index 890ff37..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/rand_util_pa_unittest.cc +++ /dev/null
@@ -1,238 +0,0 @@ -// Copyright 2011 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/rand_util.h" - -#include <stddef.h> -#include <stdint.h> - -#include <algorithm> -#include <cmath> -#include <limits> -#include <memory> -#include <vector> - -#include "base/allocator/partition_allocator/partition_alloc_base/logging.h" -#include "base/allocator/partition_allocator/partition_alloc_base/time/time.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace partition_alloc::internal::base { - -TEST(PartitionAllocBaseRandUtilTest, RandBytes) { - const size_t buffer_size = 50; - char buffer[buffer_size]; - memset(buffer, 0, buffer_size); - base::RandBytes(buffer, buffer_size); - std::sort(buffer, buffer + buffer_size); - // Probability of occurrence of less than 25 unique bytes in 50 random bytes - // is below 10^-25. - EXPECT_GT(std::unique(buffer, buffer + buffer_size) - buffer, 25); -} - -// Verify that calling base::RandBytes with an empty buffer doesn't fail. -TEST(PartitionAllocBaseRandUtilTest, RandBytes0) { - base::RandBytes(nullptr, 0); -} - -// Make sure that it is still appropriate to use RandGenerator in conjunction -// with std::random_shuffle(). -TEST(PartitionAllocBaseRandUtilTest, RandGeneratorForRandomShuffle) { - EXPECT_EQ(base::RandGenerator(1), 0U); - EXPECT_LE(std::numeric_limits<ptrdiff_t>::max(), - std::numeric_limits<int64_t>::max()); -} - -TEST(PartitionAllocBaseRandUtilTest, RandGeneratorIsUniform) { - // Verify that RandGenerator has a uniform distribution. This is a - // regression test that consistently failed when RandGenerator was - // implemented this way: - // - // return base::RandUint64() % max; - // - // A degenerate case for such an implementation is e.g. a top of - // range that is 2/3rds of the way to MAX_UINT64, in which case the - // bottom half of the range would be twice as likely to occur as the - // top half. A bit of calculus care of jar@ shows that the largest - // measurable delta is when the top of the range is 3/4ths of the - // way, so that's what we use in the test. - constexpr uint64_t kTopOfRange = - (std::numeric_limits<uint64_t>::max() / 4ULL) * 3ULL; - constexpr double kExpectedAverage = static_cast<double>(kTopOfRange / 2); - constexpr double kAllowedVariance = kExpectedAverage / 50.0; // +/- 2% - constexpr int kMinAttempts = 1000; - constexpr int kMaxAttempts = 1000000; - - double cumulative_average = 0.0; - int count = 0; - while (count < kMaxAttempts) { - uint64_t value = base::RandGenerator(kTopOfRange); - cumulative_average = (count * cumulative_average + value) / (count + 1); - - // Don't quit too quickly for things to start converging, or we may have - // a false positive. - if (count > kMinAttempts && - kExpectedAverage - kAllowedVariance < cumulative_average && - cumulative_average < kExpectedAverage + kAllowedVariance) { - break; - } - - ++count; - } - - ASSERT_LT(count, kMaxAttempts) << "Expected average was " << kExpectedAverage - << ", average ended at " << cumulative_average; -} - -TEST(PartitionAllocBaseRandUtilTest, RandUint64ProducesBothValuesOfAllBits) { - // This tests to see that our underlying random generator is good - // enough, for some value of good enough. - uint64_t kAllZeros = 0ULL; - uint64_t kAllOnes = ~kAllZeros; - uint64_t found_ones = kAllZeros; - uint64_t found_zeros = kAllOnes; - - for (size_t i = 0; i < 1000; ++i) { - uint64_t value = base::RandUint64(); - found_ones |= value; - found_zeros &= value; - - if (found_zeros == kAllZeros && found_ones == kAllOnes) - return; - } - - FAIL() << "Didn't achieve all bit values in maximum number of tries."; -} - -// Benchmark test for RandBytes(). Disabled since it's intentionally slow and -// does not test anything that isn't already tested by the existing RandBytes() -// tests. -TEST(PartitionAllocBaseRandUtilTest, DISABLED_RandBytesPerf) { - // Benchmark the performance of |kTestIterations| of RandBytes() using a - // buffer size of |kTestBufferSize|. - const int kTestIterations = 10; - const size_t kTestBufferSize = 1 * 1024 * 1024; - - std::unique_ptr<uint8_t[]> buffer(new uint8_t[kTestBufferSize]); - const TimeTicks now = TimeTicks::Now(); - for (int i = 0; i < kTestIterations; ++i) - base::RandBytes(buffer.get(), kTestBufferSize); - const TimeTicks end = TimeTicks::Now(); - - PA_LOG(INFO) << "RandBytes(" << kTestBufferSize - << ") took: " << (end - now).InMicroseconds() << "µs"; -} - -TEST(PartitionAllocBaseRandUtilTest, - InsecureRandomGeneratorProducesBothValuesOfAllBits) { - // This tests to see that our underlying random generator is good - // enough, for some value of good enough. - uint64_t kAllZeros = 0ULL; - uint64_t kAllOnes = ~kAllZeros; - uint64_t found_ones = kAllZeros; - uint64_t found_zeros = kAllOnes; - - InsecureRandomGenerator generator; - - for (size_t i = 0; i < 1000; ++i) { - uint64_t value = generator.RandUint64(); - found_ones |= value; - found_zeros &= value; - - if (found_zeros == kAllZeros && found_ones == kAllOnes) - return; - } - - FAIL() << "Didn't achieve all bit values in maximum number of tries."; -} - -namespace { - -constexpr double kXp1Percent = -2.33; -constexpr double kXp99Percent = 2.33; - -double ChiSquaredCriticalValue(double nu, double x_p) { - // From "The Art Of Computer Programming" (TAOCP), Volume 2, Section 3.3.1, - // Table 1. This is the asymptotic value for nu > 30, up to O(1 / sqrt(nu)). - return nu + sqrt(2. * nu) * x_p + 2. / 3. * (x_p * x_p) - 2. / 3.; -} - -int ExtractBits(uint64_t value, int from_bit, int num_bits) { - return (value >> from_bit) & ((1 << num_bits) - 1); -} - -// Performs a Chi-Squared test on a subset of |num_bits| extracted starting from -// |from_bit| in the generated value. -// -// See TAOCP, Volume 2, Section 3.3.1, and -// https://en.wikipedia.org/wiki/Pearson%27s_chi-squared_test for details. -// -// This is only one of the many, many random number generator test we could do, -// but they are cumbersome, as they are typically very slow, and expected to -// fail from time to time, due to their probabilistic nature. -// -// The generator we use has however been vetted with the BigCrush test suite -// from Marsaglia, so this should suffice as a smoke test that our -// implementation is wrong. -bool ChiSquaredTest(InsecureRandomGenerator& gen, - size_t n, - int from_bit, - int num_bits) { - const int range = 1 << num_bits; - PA_CHECK(static_cast<int>(n % range) == 0) << "Makes computations simpler"; - std::vector<size_t> samples(range, 0); - - // Count how many samples pf each value are found. All buckets should be - // almost equal if the generator is suitably uniformly random. - for (size_t i = 0; i < n; i++) { - int sample = ExtractBits(gen.RandUint64(), from_bit, num_bits); - samples[sample] += 1; - } - - // Compute the Chi-Squared statistic, which is: - // \Sum_{k=0}^{range-1} \frac{(count - expected)^2}{expected} - double chi_squared = 0.; - double expected_count = n / range; - for (size_t sample_count : samples) { - double deviation = sample_count - expected_count; - chi_squared += (deviation * deviation) / expected_count; - } - - // The generator should produce numbers that are not too far of (chi_squared - // lower than a given quantile), but not too close to the ideal distribution - // either (chi_squared is too low). - // - // See The Art Of Computer Programming, Volume 2, Section 3.3.1 for details. - return chi_squared > ChiSquaredCriticalValue(range - 1, kXp1Percent) && - chi_squared < ChiSquaredCriticalValue(range - 1, kXp99Percent); -} - -} // namespace - -TEST(PartitionAllocBaseRandUtilTest, InsecureRandomGeneratorChiSquared) { - constexpr int kIterations = 50; - - // Specifically test the low bits, which are usually weaker in random number - // generators. We don't use them for the 32 bit number generation, but let's - // make sure they are still suitable. - for (int start_bit : {1, 2, 3, 8, 12, 20, 32, 48, 54}) { - int pass_count = 0; - for (int i = 0; i < kIterations; i++) { - size_t samples = 1 << 16; - InsecureRandomGenerator gen; - // Fix the seed to make the test non-flaky. - gen.ReseedForTesting(kIterations + 1); - bool pass = ChiSquaredTest(gen, samples, start_bit, 8); - pass_count += pass; - } - - // We exclude 1% on each side, so we expect 98% of tests to pass, meaning 98 - // * kIterations / 100. However this is asymptotic, so add a bit of leeway. - int expected_pass_count = (kIterations * 98) / 100; - EXPECT_GE(pass_count, expected_pass_count - ((kIterations * 2) / 100)) - << "For start_bit = " << start_bit; - } -} - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/rand_util_posix.cc b/base/allocator/partition_allocator/partition_alloc_base/rand_util_posix.cc deleted file mode 100644 index 6019bbc..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/rand_util_posix.cc +++ /dev/null
@@ -1,103 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/rand_util.h" - -#include <errno.h> -#include <fcntl.h> -#include <stddef.h> -#include <stdint.h> -#include <unistd.h> -#include <sstream> - -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/files/file_util.h" -#include "base/allocator/partition_allocator/partition_alloc_base/no_destructor.h" -#include "base/allocator/partition_allocator/partition_alloc_base/posix/eintr_wrapper.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "build/build_config.h" - -#if defined(STARBOARD) -#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) -#include "third_party/lss/linux_syscall_support.h" -#elif BUILDFLAG(IS_MAC) -// TODO(crbug.com/995996): Waiting for this header to appear in the iOS SDK. -// (See below.) -#include <sys/random.h> -#endif - -namespace { - -#if BUILDFLAG(IS_AIX) -// AIX has no 64-bit support for O_CLOEXEC. -static constexpr int kOpenFlags = O_RDONLY; -#else -static constexpr int kOpenFlags = O_RDONLY | O_CLOEXEC; -#endif - -// We keep the file descriptor for /dev/urandom around so we don't need to -// reopen it (which is expensive), and since we may not even be able to reopen -// it if we are later put in a sandbox. This class wraps the file descriptor so -// we can use a static-local variable to handle opening it on the first access. -class URandomFd { - public: - URandomFd() : fd_(PA_HANDLE_EINTR(open("/dev/urandom", kOpenFlags))) { - PA_CHECK(fd_ >= 0) << "Cannot open /dev/urandom"; - } - - ~URandomFd() { close(fd_); } - - int fd() const { return fd_; } - - private: - const int fd_; -}; - -int GetUrandomFD() { - static partition_alloc::internal::base::NoDestructor<URandomFd> urandom_fd; - return urandom_fd->fd(); -} - -} // namespace - -namespace partition_alloc::internal::base { - -// NOTE: In an ideal future, all implementations of this function will just -// wrap BoringSSL's `RAND_bytes`. TODO(crbug.com/995996): Figure out the -// build/test/performance issues with dcheng's CL -// (https://chromium-review.googlesource.com/c/chromium/src/+/1545096) and land -// it or some form of it. -void RandBytes(void* output, size_t output_length) { -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) - // We have to call `getrandom` via Linux Syscall Support, rather than through - // the libc wrapper, because we might not have an up-to-date libc (e.g. on - // some bots). - const ssize_t r = PA_HANDLE_EINTR(sys_getrandom(output, output_length, 0)); - - // Return success only on total success. In case errno == ENOSYS (or any other - // error), we'll fall through to reading from urandom below. - if (output_length == static_cast<size_t>(r)) { - PA_MSAN_UNPOISON(output, output_length); - return; - } -#elif BUILDFLAG(IS_MAC) - // TODO(crbug.com/995996): Enable this on iOS too, when sys/random.h arrives - // in its SDK. - if (getentropy(output, output_length) == 0) { - return; - } -#endif - - // If the OS-specific mechanisms didn't work, fall through to reading from - // urandom. - // - // TODO(crbug.com/995996): When we no longer need to support old Linux - // kernels, we can get rid of this /dev/urandom branch altogether. - const int urandom_fd = GetUrandomFD(); - const bool success = - ReadFromFD(urandom_fd, static_cast<char*>(output), output_length); - PA_CHECK(success); -} - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/rand_util_win.cc b/base/allocator/partition_allocator/partition_alloc_base/rand_util_win.cc deleted file mode 100644 index 3c57385..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/rand_util_win.cc +++ /dev/null
@@ -1,38 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/rand_util.h" - -#include <stddef.h> -#include <stdint.h> -#include <windows.h> - -// #define needed to link in RtlGenRandom(), a.k.a. SystemFunction036. See the -// "Community Additions" comment on MSDN here: -// http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx -#define SystemFunction036 NTAPI SystemFunction036 -#include <NTSecAPI.h> -#undef SystemFunction036 - -#include <algorithm> -#include <limits> - -#include "base/allocator/partition_allocator/partition_alloc_check.h" - -namespace partition_alloc::internal::base { - -void RandBytes(void* output, size_t output_length) { - char* output_ptr = static_cast<char*>(output); - while (output_length > 0) { - const ULONG output_bytes_this_pass = static_cast<ULONG>(std::min( - output_length, static_cast<size_t>(std::numeric_limits<ULONG>::max()))); - const bool success = - RtlGenRandom(output_ptr, output_bytes_this_pass) != FALSE; - PA_CHECK(success); - output_length -= output_bytes_this_pass; - output_ptr += output_bytes_this_pass; - } -} - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/scoped_clear_last_error.h b/base/allocator/partition_allocator/partition_alloc_base/scoped_clear_last_error.h deleted file mode 100644 index c180164..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/scoped_clear_last_error.h +++ /dev/null
@@ -1,56 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_SCOPED_CLEAR_LAST_ERROR_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_SCOPED_CLEAR_LAST_ERROR_H_ - -#include <errno.h> - -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "build/build_config.h" - -namespace partition_alloc::internal::base { - -// ScopedClearLastError stores and resets the value of thread local error codes -// (errno, GetLastError()), and restores them in the destructor. This is useful -// to avoid side effects on these values in instrumentation functions that -// interact with the OS. - -// Common implementation of ScopedClearLastError for all platforms. Use -// ScopedClearLastError instead. -class PA_COMPONENT_EXPORT(PARTITION_ALLOC) ScopedClearLastErrorBase { - public: - ScopedClearLastErrorBase() : last_errno_(errno) { errno = 0; } - ScopedClearLastErrorBase(const ScopedClearLastErrorBase&) = delete; - ScopedClearLastErrorBase& operator=(const ScopedClearLastErrorBase&) = delete; - ~ScopedClearLastErrorBase() { errno = last_errno_; } - - private: - const int last_errno_; -}; - -#if BUILDFLAG(IS_WIN) - -// Windows specific implementation of ScopedClearLastError. -class PA_COMPONENT_EXPORT(PARTITION_ALLOC) ScopedClearLastError - : public ScopedClearLastErrorBase { - public: - ScopedClearLastError(); - ScopedClearLastError(const ScopedClearLastError&) = delete; - ScopedClearLastError& operator=(const ScopedClearLastError&) = delete; - ~ScopedClearLastError(); - - private: - const unsigned long last_system_error_; -}; - -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) - -using ScopedClearLastError = ScopedClearLastErrorBase; - -#endif // BUILDFLAG(IS_WIN) - -} // namespace partition_alloc::internal::base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_SCOPED_CLEAR_LAST_ERROR_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/scoped_clear_last_error_pa_unittest.cc b/base/allocator/partition_allocator/partition_alloc_base/scoped_clear_last_error_pa_unittest.cc deleted file mode 100644 index 680a086..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/scoped_clear_last_error_pa_unittest.cc +++ /dev/null
@@ -1,57 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/scoped_clear_last_error.h" - -#include "base/allocator/partition_allocator/partition_alloc_base/logging.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -#if BUILDFLAG(IS_WIN) -#include <windows.h> -#endif // BUILDFLAG(IS_WIN) - -namespace partition_alloc::internal::base { - -TEST(PAScopedClearLastError, TestNoError) { - errno = 1; - { - ScopedClearLastError clear_error; - EXPECT_EQ(0, errno); - } - EXPECT_EQ(1, errno); -} - -TEST(PAScopedClearLastError, TestError) { - errno = 1; - { - ScopedClearLastError clear_error; - errno = 2; - } - EXPECT_EQ(1, errno); -} - -#if BUILDFLAG(IS_WIN) - -TEST(PAScopedClearLastError, TestNoErrorWin) { - ::SetLastError(1); - { - ScopedClearLastError clear_error; - EXPECT_EQ(logging::SystemErrorCode(0), ::GetLastError()); - } - EXPECT_EQ(logging::SystemErrorCode(1), ::GetLastError()); -} - -TEST(PAScopedClearLastError, TestErrorWin) { - ::SetLastError(1); - { - ScopedClearLastError clear_error; - ::SetLastError(2); - } - EXPECT_EQ(logging::SystemErrorCode(1), ::GetLastError()); -} - -#endif // BUILDFLAG(IS_WIN) - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/scoped_clear_last_error_win.cc b/base/allocator/partition_allocator/partition_alloc_base/scoped_clear_last_error_win.cc deleted file mode 100644 index d3e8f8e..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/scoped_clear_last_error_win.cc +++ /dev/null
@@ -1,20 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/scoped_clear_last_error.h" - -#include <windows.h> - -namespace partition_alloc::internal::base { - -ScopedClearLastError::ScopedClearLastError() - : ScopedClearLastErrorBase(), last_system_error_(GetLastError()) { - SetLastError(0); -} - -ScopedClearLastError::~ScopedClearLastError() { - SetLastError(last_system_error_); -} - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/strings/stringprintf.cc b/base/allocator/partition_allocator/partition_alloc_base/strings/stringprintf.cc deleted file mode 100644 index 48df6d9..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/strings/stringprintf.cc +++ /dev/null
@@ -1,47 +0,0 @@ -// Copyright 2013 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/strings/stringprintf.h" - -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/scoped_clear_last_error.h" - -#include <stdarg.h> -#include <stdio.h> - -namespace partition_alloc::internal::base { - -std::string PA_PRINTF_FORMAT(1, 2) - TruncatingStringPrintf(const char* format, ...) { - base::ScopedClearLastError last_error; - char stack_buf[kMaxLengthOfTruncatingStringPrintfResult + 1]; - va_list arguments; - va_start(arguments, format); -#if BUILDFLAG(IS_WIN) - int result = vsnprintf_s(stack_buf, std::size(stack_buf), _TRUNCATE, format, - arguments); -#else - int result = vsnprintf(stack_buf, std::size(stack_buf), format, arguments); -#endif - va_end(arguments); -#if BUILDFLAG(IS_WIN) - // If an output error is encountered or data is larger than count, - // a negative value is returned. So to see whether an output error is really - // encountered or not, need to see errno. If errno == EINVAL or - // errno == ERANGE, an output error is encountered. If not, an output is - // just truncated. - if (result < 0 && (errno == EINVAL || errno == ERANGE)) - return std::string(); -#else - // If an output error is encountered, a negative value is returned. - // In the case, return an empty string. - if (result < 0) - return std::string(); -#endif - // If result is equal or larger than std::size(stack_buf), the output was - // truncated. ::base::StringPrintf doesn't truncate output. - return std::string(stack_buf); -} - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/strings/stringprintf.h b/base/allocator/partition_allocator/partition_alloc_base/strings/stringprintf.h deleted file mode 100644 index 5551fb6..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/strings/stringprintf.h +++ /dev/null
@@ -1,29 +0,0 @@ -// Copyright 2013 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_STRINGS_STRINGPRINTF_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_STRINGS_STRINGPRINTF_H_ - -#include <stdarg.h> // va_list - -#include <string> - -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "build/build_config.h" - -namespace partition_alloc::internal::base { - -// Since Only SystemErrorCodeToString and partition_alloc_perftests use -// StringPrintf, make StringPrintf not to support too long results. -// Instead, define max result length and truncate such results. -static constexpr size_t kMaxLengthOfTruncatingStringPrintfResult = 255U; - -// Return a C++ string given printf-like input. -[[nodiscard]] PA_COMPONENT_EXPORT(PARTITION_ALLOC) std::string - TruncatingStringPrintf(const char* format, ...) PA_PRINTF_FORMAT(1, 2); - -} // namespace partition_alloc::internal::base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_STRINGS_STRINGPRINTF_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/strings/stringprintf_pa_unittest.cc b/base/allocator/partition_allocator/partition_alloc_base/strings/stringprintf_pa_unittest.cc deleted file mode 100644 index 20849d6..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/strings/stringprintf_pa_unittest.cc +++ /dev/null
@@ -1,44 +0,0 @@ -// Copyright 2013 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/strings/stringprintf.h" - -#include <errno.h> -#include <stddef.h> - -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace partition_alloc::internal::base { - -TEST(PartitionAllocStringPrintfTest, TruncatingStringPrintfEmpty) { - EXPECT_EQ("", TruncatingStringPrintf("%s", "")); -} - -TEST(PartitionAllocStringPrintfTest, TruncatingStringPrintfMisc) { - EXPECT_EQ("123hello w", - TruncatingStringPrintf("%3d%2s %1c", 123, "hello", 'w')); -} - -// Test that TruncatingStringPrintf truncates too long result. -// The original TruncatingStringPrintf does not truncate. Instead, it allocates -// memory and returns an entire result. -TEST(PartitionAllocStringPrintfTest, TruncatingStringPrintfTruncatesResult) { - std::vector<char> buffer; - buffer.resize(kMaxLengthOfTruncatingStringPrintfResult + 1); - std::fill(buffer.begin(), buffer.end(), 'a'); - buffer.push_back('\0'); - std::string result = TruncatingStringPrintf("%s", buffer.data()); - EXPECT_EQ(kMaxLengthOfTruncatingStringPrintfResult, result.length()); - EXPECT_EQ(std::string::npos, result.find_first_not_of('a')); -} - -// Test that TruncatingStringPrintf does not change errno. -TEST(PartitionAllocStringPrintfTest, TruncatingStringPrintfErrno) { - errno = 1; - EXPECT_EQ("", TruncatingStringPrintf("%s", "")); - EXPECT_EQ(1, errno); -} - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/system/sys_info.h b/base/allocator/partition_allocator/partition_alloc_base/system/sys_info.h deleted file mode 100644 index d7d2437..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/system/sys_info.h +++ /dev/null
@@ -1,29 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_SYSTEM_SYS_INFO_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_SYSTEM_SYS_INFO_H_ - -#include <cstdint> - -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" - -namespace partition_alloc::internal::base { - -class PA_COMPONENT_EXPORT(PARTITION_ALLOC) SysInfo { - public: - // Retrieves detailed numeric values for the OS version. - // DON'T USE THIS ON THE MAC OR WINDOWS to determine the current OS release - // for OS version-specific feature checks and workarounds. If you must use - // an OS version check instead of a feature check, use the base::mac::IsOS* - // family from base/mac/mac_util.h, or base::win::GetVersion from - // base/win/windows_version.h. - static void OperatingSystemVersionNumbers(int32_t* major_version, - int32_t* minor_version, - int32_t* bugfix_version); -}; - -} // namespace partition_alloc::internal::base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_SYSTEM_SYS_INFO_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/system/sys_info_ios.mm b/base/allocator/partition_allocator/partition_alloc_base/system/sys_info_ios.mm deleted file mode 100644 index e6b7ce8..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/system/sys_info_ios.mm +++ /dev/null
@@ -1,24 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/system/sys_info.h" - -#import <Foundation/Foundation.h> - -#include "base/allocator/partition_allocator/partition_alloc_base/numerics/safe_conversions.h" - -namespace partition_alloc::internal::base { - -// static -void SysInfo::OperatingSystemVersionNumbers(int32_t* major_version, - int32_t* minor_version, - int32_t* bugfix_version) { - NSOperatingSystemVersion version = - [[NSProcessInfo processInfo] operatingSystemVersion]; - *major_version = saturated_cast<int32_t>(version.majorVersion); - *minor_version = saturated_cast<int32_t>(version.minorVersion); - *bugfix_version = saturated_cast<int32_t>(version.patchVersion); -} - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/system/sys_info_mac.mm b/base/allocator/partition_allocator/partition_alloc_base/system/sys_info_mac.mm deleted file mode 100644 index 100a67e..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/system/sys_info_mac.mm +++ /dev/null
@@ -1,24 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/system/sys_info.h" - -#import <Foundation/Foundation.h> - -#include "base/allocator/partition_allocator/partition_alloc_base/numerics/safe_conversions.h" - -namespace partition_alloc::internal::base { - -// static -void SysInfo::OperatingSystemVersionNumbers(int32_t* major_version, - int32_t* minor_version, - int32_t* bugfix_version) { - NSOperatingSystemVersion version = - [[NSProcessInfo processInfo] operatingSystemVersion]; - *major_version = saturated_cast<int32_t>(version.majorVersion); - *minor_version = saturated_cast<int32_t>(version.minorVersion); - *bugfix_version = saturated_cast<int32_t>(version.patchVersion); -} - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/thread_annotations.h b/base/allocator/partition_allocator/partition_alloc_base/thread_annotations.h deleted file mode 100644 index 5cf5ea0..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/thread_annotations.h +++ /dev/null
@@ -1,264 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This header file contains macro definitions for thread safety annotations -// that allow developers to document the locking policies of multi-threaded -// code. The annotations can also help program analysis tools to identify -// potential thread safety issues. -// -// Note that no analysis is done inside constructors and destructors, -// regardless of what attributes are used. See -// https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#no-checking-inside-constructors-and-destructors -// for details. -// -// Note that the annotations we use are described as deprecated in the Clang -// documentation, linked below. E.g. we use PA_EXCLUSIVE_LOCKS_REQUIRED where -// the Clang docs use REQUIRES. -// -// http://clang.llvm.org/docs/ThreadSafetyAnalysis.html -// -// We use the deprecated Clang annotations to match Abseil (relevant header -// linked below) and its ecosystem of libraries. We will follow Abseil with -// respect to upgrading to more modern annotations. -// -// https://github.com/abseil/abseil-cpp/blob/master/absl/base/thread_annotations.h -// -// These annotations are implemented using compiler attributes. Using the macros -// defined here instead of raw attributes allow for portability and future -// compatibility. -// -// When referring to mutexes in the arguments of the attributes, you should -// use variable names or more complex expressions (e.g. my_object->mutex_) -// that evaluate to a concrete mutex object whenever possible. If the mutex -// you want to refer to is not in scope, you may use a member pointer -// (e.g. &MyClass::mutex_) to refer to a mutex in some (unknown) object. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_THREAD_ANNOTATIONS_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_THREAD_ANNOTATIONS_H_ - -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "build/build_config.h" - -#if defined(__clang__) -#define PA_THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) -#else -#define PA_THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op -#endif - -// PA_GUARDED_BY() -// -// Documents if a shared field or global variable needs to be protected by a -// mutex. PA_GUARDED_BY() allows the user to specify a particular mutex that -// should be held when accessing the annotated variable. -// -// Example: -// -// Mutex mu; -// int p1 PA_GUARDED_BY(mu); -#define PA_GUARDED_BY(x) PA_THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) - -// PA_PT_GUARDED_BY() -// -// Documents if the memory location pointed to by a pointer should be guarded -// by a mutex when dereferencing the pointer. -// -// Example: -// Mutex mu; -// int *p1 PA_PT_GUARDED_BY(mu); -// -// Note that a pointer variable to a shared memory location could itself be a -// shared variable. -// -// Example: -// -// // `q`, guarded by `mu1`, points to a shared memory location that is -// // guarded by `mu2`: -// int *q PA_GUARDED_BY(mu1) PA_PT_GUARDED_BY(mu2); -#define PA_PT_GUARDED_BY(x) PA_THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x)) - -// PA_ACQUIRED_AFTER() / PA_ACQUIRED_BEFORE() -// -// Documents the acquisition order between locks that can be held -// simultaneously by a thread. For any two locks that need to be annotated -// to establish an acquisition order, only one of them needs the annotation. -// (i.e. You don't have to annotate both locks with both PA_ACQUIRED_AFTER -// and PA_ACQUIRED_BEFORE.) -// -// Example: -// -// Mutex m1; -// Mutex m2 PA_ACQUIRED_AFTER(m1); -#define PA_ACQUIRED_AFTER(...) \ - PA_THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__)) - -#define PA_ACQUIRED_BEFORE(...) \ - PA_THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__)) - -// PA_EXCLUSIVE_LOCKS_REQUIRED() / PA_SHARED_LOCKS_REQUIRED() -// -// Documents a function that expects a mutex to be held prior to entry. -// The mutex is expected to be held both on entry to, and exit from, the -// function. -// -// Example: -// -// Mutex mu1, mu2; -// int a PA_GUARDED_BY(mu1); -// int b PA_GUARDED_BY(mu2); -// -// void foo() PA_EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2) { ... }; -#define PA_EXCLUSIVE_LOCKS_REQUIRED(...) \ - PA_THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__)) - -#define PA_SHARED_LOCKS_REQUIRED(...) \ - PA_THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__)) - -// PA_LOCKS_EXCLUDED() -// -// Documents the locks acquired in the body of the function. These locks -// cannot be held when calling this function (as Abseil's `Mutex` locks are -// non-reentrant). -#define PA_LOCKS_EXCLUDED(...) \ - PA_THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__)) - -// PA_LOCK_RETURNED() -// -// Documents a function that returns a mutex without acquiring it. For example, -// a public getter method that returns a pointer to a private mutex should -// be annotated with PA_LOCK_RETURNED. -#define PA_LOCK_RETURNED(x) PA_THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) - -// PA_LOCKABLE -// -// Documents if a class/type is a lockable type (such as the `Mutex` class). -#define PA_LOCKABLE PA_THREAD_ANNOTATION_ATTRIBUTE__(lockable) - -// PA_SCOPED_LOCKABLE -// -// Documents if a class does RAII locking (such as the `MutexLock` class). -// The constructor should use `PA_*_LOCK_FUNCTION()` to specify the mutex that -// is acquired, and the destructor should use `PA_UNLOCK_FUNCTION()` with no -// arguments; the analysis will assume that the destructor unlocks whatever the -// constructor locked. -#define PA_SCOPED_LOCKABLE PA_THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) - -// PA_EXCLUSIVE_LOCK_FUNCTION() -// -// Documents functions that acquire a lock in the body of a function, and do -// not release it. -#define PA_EXCLUSIVE_LOCK_FUNCTION(...) \ - PA_THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__)) - -// PA_SHARED_LOCK_FUNCTION() -// -// Documents functions that acquire a shared (reader) lock in the body of a -// function, and do not release it. -#define PA_SHARED_LOCK_FUNCTION(...) \ - PA_THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__)) - -// PA_UNLOCK_FUNCTION() -// -// Documents functions that expect a lock to be held on entry to the function, -// and release it in the body of the function. -#define PA_UNLOCK_FUNCTION(...) \ - PA_THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__)) - -// PA_EXCLUSIVE_TRYLOCK_FUNCTION() / PA_SHARED_TRYLOCK_FUNCTION() -// -// Documents functions that try to acquire a lock, and return success or failure -// (or a non-boolean value that can be interpreted as a boolean). -// The first argument should be `true` for functions that return `true` on -// success, or `false` for functions that return `false` on success. The second -// argument specifies the mutex that is locked on success. If unspecified, this -// mutex is assumed to be `this`. -#define PA_EXCLUSIVE_TRYLOCK_FUNCTION(...) \ - PA_THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__)) - -#define PA_SHARED_TRYLOCK_FUNCTION(...) \ - PA_THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__)) - -// PA_ASSERT_EXCLUSIVE_LOCK() / PA_ASSERT_SHARED_LOCK() -// -// Documents functions that dynamically check to see if a lock is held, and fail -// if it is not held. -#define PA_ASSERT_EXCLUSIVE_LOCK(...) \ - PA_THREAD_ANNOTATION_ATTRIBUTE__(assert_exclusive_lock(__VA_ARGS__)) - -#define PA_ASSERT_SHARED_LOCK(...) \ - PA_THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_lock(__VA_ARGS__)) - -// PA_NO_THREAD_SAFETY_ANALYSIS -// -// Turns off thread safety checking within the body of a particular function. -// This annotation is used to mark functions that are known to be correct, but -// the locking behavior is more complicated than the analyzer can handle. -#define PA_NO_THREAD_SAFETY_ANALYSIS \ - PA_THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) - -//------------------------------------------------------------------------------ -// Tool-Supplied Annotations -//------------------------------------------------------------------------------ - -// PA_TS_UNCHECKED should be placed around lock expressions that are not valid -// C++ syntax, but which are present for documentation purposes. These -// annotations will be ignored by the analysis. -#define PA_TS_UNCHECKED(x) "" - -// PA_TS_FIXME is used to mark lock expressions that are not valid C++ syntax. -// It is used by automated tools to mark and disable invalid expressions. -// The annotation should either be fixed, or changed to PA_TS_UNCHECKED. -#define PA_TS_FIXME(x) "" - -// Like PA_NO_THREAD_SAFETY_ANALYSIS, this turns off checking within the body of -// a particular function. However, this attribute is used to mark functions -// that are incorrect and need to be fixed. It is used by automated tools to -// avoid breaking the build when the analysis is updated. -// Code owners are expected to eventually fix the routine. -#define PA_NO_THREAD_SAFETY_ANALYSIS_FIXME PA_NO_THREAD_SAFETY_ANALYSIS - -// Similar to PA_NO_THREAD_SAFETY_ANALYSIS_FIXME, this macro marks a -// PA_GUARDED_BY annotation that needs to be fixed, because it is producing -// thread safety warning. It disables the PA_GUARDED_BY. -#define PA_GUARDED_BY_FIXME(x) - -// Disables warnings for a single read operation. This can be used to avoid -// warnings when it is known that the read is not actually involved in a race, -// but the compiler cannot confirm that. -#define PA_TS_UNCHECKED_READ(x) \ - partition_alloc::internal::thread_safety_analysis::ts_unchecked_read(x) - -namespace partition_alloc::internal::thread_safety_analysis { - -// Takes a reference to a guarded data member, and returns an unguarded -// reference. -template <typename T> -inline const T& ts_unchecked_read(const T& v) PA_NO_THREAD_SAFETY_ANALYSIS { - return v; -} - -template <typename T> -inline T& ts_unchecked_read(T& v) PA_NO_THREAD_SAFETY_ANALYSIS { - return v; -} - -} // namespace partition_alloc::internal::thread_safety_analysis - -// The above is imported as-is from abseil-cpp. The following Chromium-specific -// synonyms are added for Chromium concepts (SequenceChecker/ThreadChecker). -#if BUILDFLAG(PA_DCHECK_IS_ON) - -// Equivalent to PA_GUARDED_BY for SequenceChecker/ThreadChecker. Currently, -#define PA_GUARDED_BY_CONTEXT(name) PA_GUARDED_BY(name) - -// Equivalent to PA_EXCLUSIVE_LOCKS_REQUIRED for SequenceChecker/ThreadChecker. -#define PA_VALID_CONTEXT_REQUIRED(name) PA_EXCLUSIVE_LOCKS_REQUIRED(name) - -#else // BUILDFLAG(PA_DCHECK_IS_ON) - -#define PA_GUARDED_BY_CONTEXT(name) -#define PA_VALID_CONTEXT_REQUIRED(name) - -#endif // BUILDFLAG(PA_DCHECK_IS_ON) - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_THREAD_ANNOTATIONS_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/thread_annotations_pa_unittest.cc b/base/allocator/partition_allocator/partition_alloc_base/thread_annotations_pa_unittest.cc deleted file mode 100644 index 2f0ae82..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/thread_annotations_pa_unittest.cc +++ /dev/null
@@ -1,58 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/thread_annotations.h" - -#include "testing/gtest/include/gtest/gtest.h" - -namespace { - -class PA_LOCKABLE Lock { - public: - void Acquire() PA_EXCLUSIVE_LOCK_FUNCTION() {} - void Release() PA_UNLOCK_FUNCTION() {} -}; - -class PA_SCOPED_LOCKABLE AutoLock { - public: - AutoLock(Lock& lock) PA_EXCLUSIVE_LOCK_FUNCTION(lock) : lock_(lock) { - lock.Acquire(); - } - ~AutoLock() PA_UNLOCK_FUNCTION() { lock_.Release(); } - - private: - Lock& lock_; -}; - -class ThreadSafe { - public: - void ExplicitIncrement(); - void ImplicitIncrement(); - - private: - Lock lock_; - int counter_ PA_GUARDED_BY(lock_); -}; - -void ThreadSafe::ExplicitIncrement() { - lock_.Acquire(); - ++counter_; - lock_.Release(); -} - -void ThreadSafe::ImplicitIncrement() { - AutoLock auto_lock(lock_); - counter_++; -} - -TEST(PartitionAllocThreadAnnotationsTest, ExplicitIncrement) { - ThreadSafe thread_safe; - thread_safe.ExplicitIncrement(); -} -TEST(PartitionAllocThreadAnnotationsTest, ImplicitIncrement) { - ThreadSafe thread_safe; - thread_safe.ImplicitIncrement(); -} - -} // anonymous namespace
diff --git a/base/allocator/partition_allocator/partition_alloc_base/thread_annotations_pa_unittest.nc b/base/allocator/partition_allocator/partition_alloc_base/thread_annotations_pa_unittest.nc deleted file mode 100644 index cbf4fbb..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/thread_annotations_pa_unittest.nc +++ /dev/null
@@ -1,71 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This is a "No Compile Test" suite. -// https://dev.chromium.org/developers/testing/no-compile-tests - -#include "base/allocator/partition_allocator/partition_alloc_base/thread_annotations.h" - -namespace { - -class PA_LOCKABLE Lock { - public: - void Acquire() PA_EXCLUSIVE_LOCK_FUNCTION() {} - void Release() PA_UNLOCK_FUNCTION() {} -}; - -class PA_SCOPED_LOCKABLE AutoLock { - public: - AutoLock(Lock& lock) PA_EXCLUSIVE_LOCK_FUNCTION(lock) : lock_(lock) { - lock.Acquire(); - } - ~AutoLock() PA_UNLOCK_FUNCTION() { lock_.Release(); } - - private: - Lock& lock_; -}; -class ThreadSafe { - public: - void BuggyIncrement(); - private: - Lock lock_; - int counter_ PA_GUARDED_BY(lock_); -}; - -#if defined(NCTEST_LOCK_WITHOUT_UNLOCK) // [r"fatal error: mutex 'lock_' is still held at the end of function"] - -void ThreadSafe::BuggyIncrement() { - lock_.Acquire(); - ++counter_; - // Forgot to release the lock. -} - -#elif defined(NCTEST_ACCESS_WITHOUT_LOCK) // [r"fatal error: writing variable 'counter_' requires holding mutex 'lock_' exclusively"] - -void ThreadSafe::BuggyIncrement() { - // Member access without holding the lock guarding it. - ++counter_; -} - -#elif defined(NCTEST_ACCESS_WITHOUT_SCOPED_LOCK) // [r"fatal error: writing variable 'counter_' requires holding mutex 'lock_' exclusively"] - -void ThreadSafe::BuggyIncrement() { - { - AutoLock auto_lock(lock_); - // The AutoLock will go out of scope before the guarded member access. - } - ++counter_; -} - -#elif defined(NCTEST_GUARDED_BY_WRONG_TYPE) // [r"fatal error: 'guarded_by' attribute requires arguments whose type is annotated"] - -int not_lockable; -int global_counter PA_GUARDED_BY(not_lockable); - -// Defined to avoid link error. -void ThreadSafe::BuggyIncrement() { } - -#endif - -} // anonymous namespace
diff --git a/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread.cc b/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread.cc deleted file mode 100644 index 2aa8c6a..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread.cc +++ /dev/null
@@ -1,29 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread.h" - -namespace partition_alloc::internal::base { - -namespace { - -// SetThreadNameHook is invoked by EnablePCScan(). EnablePCScan() will be -// invoked soon after running RunBrowser, RunZygote, and RunContentProcess. -// So g_set_thread_name_proc can be non-atomic. -SetThreadNameProc g_set_thread_name_proc = nullptr; - -} // namespace - -void PlatformThread::SetThreadNameHook(SetThreadNameProc hook) { - g_set_thread_name_proc = hook; -} - -// static -void PlatformThread::SetName(const std::string& name) { - if (!g_set_thread_name_proc) - return; - g_set_thread_name_proc(name); -} - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread.h b/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread.h deleted file mode 100644 index affac9b..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread.h +++ /dev/null
@@ -1,116 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// WARNING: You should *NOT* be using this class directly. PlatformThread is -// the low-level platform-specific abstraction to the OS's threading interface. -// You should instead be using a message-loop driven Thread, see thread.h. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_THREADING_PLATFORM_THREAD_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_THREADING_PLATFORM_THREAD_H_ - -#include <stddef.h> - -#include <iosfwd> - -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_ref.h" -#include "base/allocator/partition_allocator/partition_alloc_base/time/time.h" -#include "build/build_config.h" - -#if defined(STARBOARD) -#include "starboard/thread.h" -#elif BUILDFLAG(IS_WIN) -#include "base/allocator/partition_allocator/partition_alloc_base/win/windows_types.h" -#elif BUILDFLAG(IS_FUCHSIA) -#include <zircon/types.h> -#elif BUILDFLAG(IS_APPLE) -#include <mach/mach_types.h> -#elif BUILDFLAG(IS_POSIX) -#include <pthread.h> -#include <unistd.h> -#endif - -namespace partition_alloc::internal::base { - -// Used for logging. Always an integer value. -#if defined(STARBOARD) -typedef SbThreadId PlatformThreadId; -#elif BUILDFLAG(IS_WIN) -typedef DWORD PlatformThreadId; -#elif BUILDFLAG(IS_FUCHSIA) -typedef zx_handle_t PlatformThreadId; -#elif BUILDFLAG(IS_APPLE) -typedef mach_port_t PlatformThreadId; -#elif BUILDFLAG(IS_POSIX) -typedef pid_t PlatformThreadId; -#endif - -// Used to operate on threads. -class PlatformThreadHandle { - public: -#if defined(STARBOARD) - typedef SbThread Handle; -#elif BUILDFLAG(IS_WIN) - typedef void* Handle; -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) - typedef pthread_t Handle; -#endif - - constexpr PlatformThreadHandle() : handle_(0) {} - - explicit constexpr PlatformThreadHandle(Handle handle) : handle_(handle) {} - - bool is_equal(const PlatformThreadHandle& other) const { - return handle_ == other.handle_; - } - - bool is_null() const { return !handle_; } - - Handle platform_handle() const { return handle_; } - - private: - Handle handle_; -}; - -const PlatformThreadId kInvalidThreadId(0); - -typedef void (*SetThreadNameProc)(const std::string&); - -// A namespace for low-level thread functions. -class PA_COMPONENT_EXPORT(PARTITION_ALLOC) PlatformThread { - public: - PlatformThread() = delete; - PlatformThread(const PlatformThread&) = delete; - PlatformThread& operator=(const PlatformThread&) = delete; - - // Gets the current thread id, which may be useful for logging purposes. - static PlatformThreadId CurrentId(); - - // Gets the current thread reference, which can be used to check if - // we're on the right thread quickly. - static PlatformThreadRef CurrentRef(); - - // Get the handle representing the current thread. On Windows, this is a - // pseudo handle constant which will always represent the thread using it and - // hence should not be shared with other threads nor be used to differentiate - // the current thread from another. - static PlatformThreadHandle CurrentHandle(); - - // Sleeps for the specified duration (real-time; ignores time overrides). - // Note: The sleep duration may be in base::Time or base::TimeTicks, depending - // on platform. If you're looking to use this in unit tests testing delayed - // tasks, this will be unreliable - instead, use - // base::test::TaskEnvironment with MOCK_TIME mode. - static void Sleep(TimeDelta duration); - - // Sets the thread name visible to debuggers/tools. This will try to - // initialize the context for current thread unless it's a WorkerThread. - static void SetName(const std::string& name); - - static void SetThreadNameHook(SetThreadNameProc hook); -}; - -} // namespace partition_alloc::internal::base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_THREADING_PLATFORM_THREAD_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_android_for_testing.cc b/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_android_for_testing.cc deleted file mode 100644 index 8b613c2..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_android_for_testing.cc +++ /dev/null
@@ -1,30 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_for_testing.h" - -#include <pthread.h> -#include <stddef.h> - -namespace partition_alloc::internal::base { - -void InitThreading() {} - -void TerminateOnThread() { - // partition alloc tests don't use AttachCurrentThread(), because - // the tests don't set / get any thread priority. So no need to do - // "base::android::DetachFromVM();" here. -} - -size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes) { -#if !defined(ADDRESS_SANITIZER) - return 0; -#else - // AddressSanitizer bloats the stack approximately 2x. Default stack size of - // 1Mb is not enough for some tests (see http://crbug.com/263749 for example). - return 2 * (1 << 20); // 2Mb -#endif -} - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_for_testing.h b/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_for_testing.h deleted file mode 100644 index 57b85ea..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_for_testing.h +++ /dev/null
@@ -1,73 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// WARNING: You should *NOT* be using this class directly. PlatformThread is -// the low-level platform-specific abstraction to the OS's threading interface. -// You should instead be using a message-loop driven Thread, see thread.h. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_THREADING_PLATFORM_THREAD_FOR_TESTING_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_THREADING_PLATFORM_THREAD_FOR_TESTING_H_ - -#include <stddef.h> - -#include <iosfwd> - -#include "base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread.h" -#include "build/build_config.h" - -namespace partition_alloc::internal::base { - -// A namespace for low-level thread functions. -class PlatformThreadForTesting : public PlatformThread { - public: - // Implement this interface to run code on a background thread. Your - // ThreadMain method will be called on the newly created thread. - class Delegate { - public: - virtual void ThreadMain() = 0; - - protected: - virtual ~Delegate() = default; - }; - - PlatformThreadForTesting() = delete; - PlatformThreadForTesting(const PlatformThreadForTesting&) = delete; - PlatformThreadForTesting& operator=(const PlatformThreadForTesting&) = delete; - - // Yield the current thread so another thread can be scheduled. - // - // Note: this is likely not the right call to make in most situations. If this - // is part of a spin loop, consider base::Lock, which likely has better tail - // latency. Yielding the thread has different effects depending on the - // platform, system load, etc., and can result in yielding the CPU for less - // than 1us, or many tens of ms. - static void YieldCurrentThread(); - - // Creates a new thread. The `stack_size` parameter can be 0 to indicate - // that the default stack size should be used. Upon success, - // `*thread_handle` will be assigned a handle to the newly created thread, - // and `delegate`'s ThreadMain method will be executed on the newly created - // thread. - // NOTE: When you are done with the thread handle, you must call Join to - // release system resources associated with the thread. You must ensure that - // the Delegate object outlives the thread. - static bool Create(size_t stack_size, - Delegate* delegate, - PlatformThreadHandle* thread_handle); - - // Joins with a thread created via the Create function. This function blocks - // the caller until the designated thread exits. This will invalidate - // `thread_handle`. - static void Join(PlatformThreadHandle thread_handle); - -#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) - // Returns the default thread stack size set by chrome. If we do not - // explicitly set default size then returns 0. - static size_t GetDefaultThreadStackSize(); -#endif -}; - -} // namespace partition_alloc::internal::base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_THREADING_PLATFORM_THREADD_FOR_TESTING_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_fuchsia_for_testing.cc b/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_fuchsia_for_testing.cc deleted file mode 100644 index 7c1c401..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_fuchsia_for_testing.cc +++ /dev/null
@@ -1,20 +0,0 @@ -// Copyright 2017 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_for_testing.h" - -#include <pthread.h> -#include <stddef.h> - -namespace partition_alloc::internal::base { - -void InitThreading() {} - -void TerminateOnThread() {} - -size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes) { - return 0; -} - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_internal_posix.h b/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_internal_posix.h deleted file mode 100644 index e7d2909..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_internal_posix.h +++ /dev/null
@@ -1,24 +0,0 @@ -// Copyright 2015 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_THREADING_PLATFORM_THREAD_INTERNAL_POSIX_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_THREADING_PLATFORM_THREAD_INTERNAL_POSIX_H_ - -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "build/build_config.h" - -namespace partition_alloc::internal::base::internal { - -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) -// Current thread id is cached in thread local storage for performance reasons. -// In some rare cases it's important to invalidate that cache explicitly (e.g. -// after going through clone() syscall which does not call pthread_atfork() -// handlers). -// This can only be called when the process is single-threaded. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) void InvalidateTidCache(); -#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) - -} // namespace partition_alloc::internal::base::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_THREADING_PLATFORM_THREAD_INTERNAL_POSIX_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_linux_for_testing.cc b/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_linux_for_testing.cc deleted file mode 100644 index 6218bad..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_linux_for_testing.cc +++ /dev/null
@@ -1,26 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_for_testing.h" - -#include <pthread.h> -#include <stddef.h> - -namespace partition_alloc::internal::base { - -void InitThreading() {} - -void TerminateOnThread() {} - -size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes) { -#if !defined(THREAD_SANITIZER) - return 0; -#else // defined(THREAD_SANITIZER) - // ThreadSanitizer bloats the stack heavily. Evidence has been that the - // default stack size isn't enough for some browser tests. - return 2 * (1 << 23); // 2 times 8192K (the default stack size on Linux). -#endif // defined(THREAD_SANITIZER) -} - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_mac_for_testing.mm b/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_mac_for_testing.mm deleted file mode 100644 index 1b53bc7..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_mac_for_testing.mm +++ /dev/null
@@ -1,91 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_for_testing.h" - -#import <Foundation/Foundation.h> -#include <mach/mach.h> -#include <mach/mach_time.h> -#include <mach/thread_policy.h> -#include <mach/thread_switch.h> -#include <stddef.h> -#include <sys/resource.h> - -#include <algorithm> -#include <atomic> - -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "build/build_config.h" - -namespace partition_alloc::internal::base { - -// If Cocoa is to be used on more than one thread, it must know that the -// application is multithreaded. Since it's possible to enter Cocoa code -// from threads created by pthread_thread_create, Cocoa won't necessarily -// be aware that the application is multithreaded. Spawning an NSThread is -// enough to get Cocoa to set up for multithreaded operation, so this is done -// if necessary before pthread_thread_create spawns any threads. -// -// http://developer.apple.com/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/chapter_4_section_4.html -void InitThreading() { - static BOOL multithreaded = [NSThread isMultiThreaded]; - if (!multithreaded) { - // +[NSObject class] is idempotent. - [NSThread detachNewThreadSelector:@selector(class) - toTarget:[NSObject class] - withObject:nil]; - multithreaded = YES; - - PA_DCHECK([NSThread isMultiThreaded]); - } -} - -// static -void PlatformThreadForTesting::YieldCurrentThread() { - // Don't use sched_yield(), as it can lead to 10ms delays. - // - // This only depresses the thread priority for 1ms, which is more in line - // with what calling code likely wants. See this bug in webkit for context: - // https://bugs.webkit.org/show_bug.cgi?id=204871 - mach_msg_timeout_t timeout_ms = 1; - thread_switch(MACH_PORT_NULL, SWITCH_OPTION_DEPRESS, timeout_ms); -} - -size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes) { -#if BUILDFLAG(IS_IOS) - return 0; -#else - // The Mac OS X default for a pthread stack size is 512kB. - // Libc-594.1.4/pthreads/pthread.c's pthread_attr_init uses - // DEFAULT_STACK_SIZE for this purpose. - // - // 512kB isn't quite generous enough for some deeply recursive threads that - // otherwise request the default stack size by specifying 0. Here, adopt - // glibc's behavior as on Linux, which is to use the current stack size - // limit (ulimit -s) as the default stack size. See - // glibc-2.11.1/nptl/nptl-init.c's __pthread_initialize_minimal_internal. To - // avoid setting the limit below the Mac OS X default or the minimum usable - // stack size, these values are also considered. If any of these values - // can't be determined, or if stack size is unlimited (ulimit -s unlimited), - // stack_size is left at 0 to get the system default. - // - // Mac OS X normally only applies ulimit -s to the main thread stack. On - // contemporary OS X and Linux systems alike, this value is generally 8MB - // or in that neighborhood. - size_t default_stack_size = 0; - struct rlimit stack_rlimit; - if (pthread_attr_getstacksize(&attributes, &default_stack_size) == 0 && - getrlimit(RLIMIT_STACK, &stack_rlimit) == 0 && - stack_rlimit.rlim_cur != RLIM_INFINITY) { - default_stack_size = std::max( - std::max(default_stack_size, static_cast<size_t>(PTHREAD_STACK_MIN)), - static_cast<size_t>(stack_rlimit.rlim_cur)); - } - return default_stack_size; -#endif -} - -void TerminateOnThread() {} - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_posix.cc b/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_posix.cc deleted file mode 100644 index 46cbada..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_posix.cc +++ /dev/null
@@ -1,146 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread.h" - -#include <errno.h> -#include <pthread.h> -#include <stddef.h> -#include <stdint.h> -#include <sys/time.h> -#include <sys/types.h> -#include <unistd.h> - -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_base/logging.h" -#include "base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_internal_posix.h" -#include "build/build_config.h" - -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) -#include <sys/syscall.h> -#include <atomic> -#endif - -#if BUILDFLAG(IS_FUCHSIA) -#include <zircon/process.h> -#endif - -namespace partition_alloc::internal::base { - -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) - -namespace { - -// Store the thread ids in local storage since calling the SWI can be -// expensive and PlatformThread::CurrentId is used liberally. -thread_local pid_t g_thread_id = -1; - -// A boolean value that indicates that the value stored in |g_thread_id| on the -// main thread is invalid, because it hasn't been updated since the process -// forked. -// -// This used to work by setting |g_thread_id| to -1 in a pthread_atfork handler. -// However, when a multithreaded process forks, it is only allowed to call -// async-signal-safe functions until it calls an exec() syscall. However, -// accessing TLS may allocate (see crbug.com/1275748), which is not -// async-signal-safe and therefore causes deadlocks, corruption, and crashes. -// -// It's Atomic to placate TSAN. -std::atomic<bool> g_main_thread_tid_cache_valid = false; - -// Tracks whether the current thread is the main thread, and therefore whether -// |g_main_thread_tid_cache_valid| is relevant for the current thread. This is -// also updated by PlatformThread::CurrentId(). -thread_local bool g_is_main_thread = true; - -class InitAtFork { - public: - InitAtFork() { - pthread_atfork(nullptr, nullptr, internal::InvalidateTidCache); - } -}; - -} // namespace - -namespace internal { - -void InvalidateTidCache() { - g_main_thread_tid_cache_valid.store(false, std::memory_order_relaxed); -} - -} // namespace internal - -#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) - -// static -PlatformThreadId PlatformThread::CurrentId() { - // Pthreads doesn't have the concept of a thread ID, so we have to reach down - // into the kernel. -#if BUILDFLAG(IS_APPLE) - return pthread_mach_thread_np(pthread_self()); -#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) - static InitAtFork init_at_fork; - if (g_thread_id == -1 || - (g_is_main_thread && - !g_main_thread_tid_cache_valid.load(std::memory_order_relaxed))) { - // Update the cached tid. - g_thread_id = syscall(__NR_gettid); - // If this is the main thread, we can mark the tid_cache as valid. - // Otherwise, stop the current thread from always entering this slow path. - if (g_thread_id == getpid()) { - g_main_thread_tid_cache_valid.store(true, std::memory_order_relaxed); - } else { - g_is_main_thread = false; - } - } else { -#if BUILDFLAG(PA_DCHECK_IS_ON) - if (g_thread_id != syscall(__NR_gettid)) { - PA_RAW_LOG( - FATAL, - "Thread id stored in TLS is different from thread id returned by " - "the system. It is likely that the process was forked without going " - "through fork()."); - } -#endif - } - return g_thread_id; -#elif BUILDFLAG(IS_ANDROID) - // Note: do not cache the return value inside a thread_local variable on - // Android (as above). The reasons are: - // - thread_local is slow on Android (goes through emutls) - // - gettid() is fast, since its return value is cached in pthread (in the - // thread control block of pthread). See gettid.c in bionic. - return gettid(); -#elif BUILDFLAG(IS_FUCHSIA) - return zx_thread_self(); -#elif BUILDFLAG(IS_SOLARIS) || BUILDFLAG(IS_QNX) - return pthread_self(); -#elif BUILDFLAG(IS_POSIX) && BUILDFLAG(IS_AIX) - return pthread_self(); -#elif BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_AIX) - return reinterpret_cast<int64_t>(pthread_self()); -#endif -} - -// static -PlatformThreadRef PlatformThread::CurrentRef() { - return PlatformThreadRef(pthread_self()); -} - -// static -void PlatformThread::Sleep(TimeDelta duration) { - struct timespec sleep_time, remaining; - - // Break the duration into seconds and nanoseconds. - // NOTE: TimeDelta's microseconds are int64s while timespec's - // nanoseconds are longs, so this unpacking must prevent overflow. - sleep_time.tv_sec = duration.InSeconds(); - duration -= Seconds(sleep_time.tv_sec); - sleep_time.tv_nsec = duration.InMicroseconds() * 1000; // nanoseconds - - while (nanosleep(&sleep_time, &remaining) == -1 && errno == EINTR) - sleep_time = remaining; -} - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_posix_for_testing.cc b/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_posix_for_testing.cc deleted file mode 100644 index c285665..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_posix_for_testing.cc +++ /dev/null
@@ -1,149 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_for_testing.h" - -#include <errno.h> -#include <pthread.h> -#include <sched.h> -#include <stddef.h> -#include <stdint.h> -#include <sys/time.h> -#include <sys/types.h> -#include <unistd.h> -#include <memory> - -#include "base/allocator/partition_allocator/partition_alloc_base/logging.h" -#include "base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_internal_posix.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "build/build_config.h" - -#if BUILDFLAG(IS_FUCHSIA) -#include <zircon/process.h> -#else -#include <sys/resource.h> -#endif - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && BUILDFLAG(USE_STARSCAN) -#include "base/allocator/partition_allocator/starscan/pcscan.h" -#include "base/allocator/partition_allocator/starscan/stack/stack.h" -#endif - -namespace partition_alloc::internal::base { - -void InitThreading(); -void TerminateOnThread(); -size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes); - -namespace { - -struct ThreadParams { - PlatformThreadForTesting::Delegate* delegate = nullptr; -}; - -void* ThreadFunc(void* params) { - PlatformThreadForTesting::Delegate* delegate = nullptr; - - { - std::unique_ptr<ThreadParams> thread_params( - static_cast<ThreadParams*>(params)); - - delegate = thread_params->delegate; - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && BUILDFLAG(USE_STARSCAN) - PCScan::NotifyThreadCreated(GetStackPointer()); -#endif - } - - delegate->ThreadMain(); - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && BUILDFLAG(USE_STARSCAN) - PCScan::NotifyThreadDestroyed(); -#endif - - TerminateOnThread(); - return nullptr; -} - -bool CreateThread(size_t stack_size, - PlatformThreadForTesting::Delegate* delegate, - PlatformThreadHandle* thread_handle) { - PA_DCHECK(thread_handle); - base::InitThreading(); - - pthread_attr_t attributes; - pthread_attr_init(&attributes); - - // Get a better default if available. - if (stack_size == 0) { - stack_size = base::GetDefaultThreadStackSize(attributes); - } - - if (stack_size > 0) { - pthread_attr_setstacksize(&attributes, stack_size); - } - - std::unique_ptr<ThreadParams> params(new ThreadParams); - params->delegate = delegate; - - pthread_t handle; - int err = pthread_create(&handle, &attributes, ThreadFunc, params.get()); - bool success = !err; - if (success) { - // ThreadParams should be deleted on the created thread after used. - std::ignore = params.release(); - } else { - // Value of |handle| is undefined if pthread_create fails. - handle = 0; - errno = err; - PA_PLOG(ERROR) << "pthread_create"; - } - *thread_handle = PlatformThreadHandle(handle); - - pthread_attr_destroy(&attributes); - - return success; -} - -} // namespace - -#if !BUILDFLAG(IS_APPLE) -// static -void PlatformThreadForTesting::YieldCurrentThread() { - sched_yield(); -} -#endif // !BUILDFLAG(IS_APPLE) - -// static -bool PlatformThreadForTesting::Create(size_t stack_size, - Delegate* delegate, - PlatformThreadHandle* thread_handle) { - return CreateThread(stack_size, delegate, thread_handle); -} - -// static -void PlatformThreadForTesting::Join(PlatformThreadHandle thread_handle) { - // Joining another thread may block the current thread for a long time, since - // the thread referred to by |thread_handle| may still be running long-lived / - // blocking tasks. - - // Remove ScopedBlockingCallWithBaseSyncPrimitives, because only partition - // alloc tests use PlatformThread::Join. So there is no special requirement - // to monitor blocking calls - // (by using ThreadGroupImpl::WorkerThreadDelegateImpl). - // - // base::internal::ScopedBlockingCallWithBaseSyncPrimitives - // scoped_blocking_call(base::BlockingType::MAY_BLOCK); - PA_CHECK(0 == pthread_join(thread_handle.platform_handle(), nullptr)); -} - -// static -size_t PlatformThreadForTesting::GetDefaultThreadStackSize() { - pthread_attr_t attributes; - pthread_attr_init(&attributes); - return base::GetDefaultThreadStackSize(attributes); -} - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_ref.h b/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_ref.h deleted file mode 100644 index 3fec951..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_ref.h +++ /dev/null
@@ -1,60 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// WARNING: *DO NOT* use this class directly. base::PlatformThreadRef is a -// low-level platform-specific abstraction to the OS's threading interface. -// Instead, consider using a message-loop driven base::Thread, see -// base/threading/thread.h. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_THREADING_PLATFORM_THREAD_REF_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_THREADING_PLATFORM_THREAD_REF_H_ - -#include <iosfwd> - -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "build/build_config.h" - -#if defined(STARBOARD) -#include "starboard/thread.h" -#elif BUILDFLAG(IS_WIN) -#include "base/allocator/partition_allocator/partition_alloc_base/win/windows_types.h" -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) -#include <pthread.h> -#endif - -namespace partition_alloc::internal::base { - -// Used for thread checking and debugging. -// Meant to be as fast as possible. -// These are produced by PlatformThread::CurrentRef(), and used to later -// check if we are on the same thread or not by using ==. These are safe -// to copy between threads, but can't be copied to another process as they -// have no meaning there. Also, the internal identifier can be re-used -// after a thread dies, so a PlatformThreadRef cannot be reliably used -// to distinguish a new thread from an old, dead thread. -class PlatformThreadRef { - public: -#if defined(STARBOARD) - typedef SbThread RefType; -#elif BUILDFLAG(IS_WIN) - using RefType = DWORD; -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) - using RefType = pthread_t; -#endif - - constexpr PlatformThreadRef() = default; - explicit constexpr PlatformThreadRef(RefType id) : id_(id) {} - - bool operator==(PlatformThreadRef other) const { return id_ == other.id_; } - bool operator!=(PlatformThreadRef other) const { return id_ != other.id_; } - - bool is_null() const { return id_ == 0; } - - private: - RefType id_ = 0; -}; - -} // namespace partition_alloc::internal::base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_THREADING_PLATFORM_THREAD_REF_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_win.cc b/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_win.cc deleted file mode 100644 index c5ff387..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_win.cc +++ /dev/null
@@ -1,43 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread.h" - -#include <stddef.h> - -#include "base/allocator/partition_allocator/partition_alloc_base/time/time_override.h" - -#include <windows.h> - -namespace partition_alloc::internal::base { - -// static -PlatformThreadId PlatformThread::CurrentId() { - return ::GetCurrentThreadId(); -} - -// static -PlatformThreadRef PlatformThread::CurrentRef() { - return PlatformThreadRef(::GetCurrentThreadId()); -} - -// static -PlatformThreadHandle PlatformThread::CurrentHandle() { - return PlatformThreadHandle(::GetCurrentThread()); -} - -// static -void PlatformThread::Sleep(TimeDelta duration) { - // When measured with a high resolution clock, Sleep() sometimes returns much - // too early. We may need to call it repeatedly to get the desired duration. - // PlatformThread::Sleep doesn't support mock-time, so this always uses - // real-time. - const TimeTicks end = subtle::TimeTicksNowIgnoringOverride() + duration; - for (TimeTicks now = subtle::TimeTicksNowIgnoringOverride(); now < end; - now = subtle::TimeTicksNowIgnoringOverride()) { - ::Sleep(static_cast<DWORD>((end - now).InMillisecondsRoundedUp())); - } -} - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_win_for_testing.cc b/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_win_for_testing.cc deleted file mode 100644 index 3d63115..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_win_for_testing.cc +++ /dev/null
@@ -1,193 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_for_testing.h" - -#include <stddef.h> - -#include "base/allocator/partition_allocator/oom.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/alias.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "build/build_config.h" - -#include <windows.h> - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && BUILDFLAG(USE_STARSCAN) -#include "base/allocator/partition_allocator/starscan/pcscan.h" -#include "base/allocator/partition_allocator/starscan/stack/stack.h" -#endif - -namespace partition_alloc::internal::base { - -namespace { - -// base/win/scoped_handle.h looks too much to just run partition_alloc -// tests. -class ScopedHandle { - public: - ScopedHandle() : handle_(INVALID_HANDLE_VALUE) {} - - ~ScopedHandle() { - if (handle_ != INVALID_HANDLE_VALUE) - CloseHandle(handle_); - handle_ = INVALID_HANDLE_VALUE; - } - - void Set(HANDLE handle) { - if (handle != handle_) { - if (handle != INVALID_HANDLE_VALUE) - CloseHandle(handle_); - handle_ = handle; - } - } - - private: - HANDLE handle_; -}; - -struct ThreadParams { - PlatformThreadForTesting::Delegate* delegate = nullptr; -}; - -DWORD __stdcall ThreadFunc(void* params) { - ThreadParams* thread_params = static_cast<ThreadParams*>(params); - PlatformThreadForTesting::Delegate* delegate = thread_params->delegate; - - // Retrieve a copy of the thread handle to use as the key in the - // thread name mapping. - PlatformThreadHandle::Handle platform_handle; - BOOL did_dup = DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), - GetCurrentProcess(), &platform_handle, 0, - FALSE, DUPLICATE_SAME_ACCESS); - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && BUILDFLAG(USE_STARSCAN) - PCScan::NotifyThreadCreated(GetStackPointer()); -#endif - - ScopedHandle scoped_platform_handle; - if (did_dup) { - scoped_platform_handle.Set(platform_handle); - } - - delete thread_params; - delegate->ThreadMain(); - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && BUILDFLAG(USE_STARSCAN) - PCScan::NotifyThreadDestroyed(); -#endif - return 0; -} - -// CreateThreadInternal() matches PlatformThread::CreateWithPriority(), except -// that |out_thread_handle| may be nullptr, in which case a non-joinable thread -// is created. -bool CreateThreadInternal(size_t stack_size, - PlatformThreadForTesting::Delegate* delegate, - PlatformThreadHandle* out_thread_handle) { - unsigned int flags = 0; - if (stack_size > 0) { - flags = STACK_SIZE_PARAM_IS_A_RESERVATION; -#if defined(ARCH_CPU_32_BITS) - } else { - // The process stack size is increased to give spaces to |RendererMain| in - // |chrome/BUILD.gn|, but keep the default stack size of other threads to - // 1MB for the address space pressure. - flags = STACK_SIZE_PARAM_IS_A_RESERVATION; - static BOOL is_wow64 = -1; - if (is_wow64 == -1 && !IsWow64Process(GetCurrentProcess(), &is_wow64)) - is_wow64 = FALSE; - // When is_wow64 is set that means we are running on 64-bit Windows and we - // get 4 GiB of address space. In that situation we can afford to use 1 MiB - // of address space for stacks. When running on 32-bit Windows we only get - // 2 GiB of address space so we need to conserve. Typically stack usage on - // these threads is only about 100 KiB. - if (is_wow64) - stack_size = 1024 * 1024; - else - stack_size = 512 * 1024; -#endif - } - - ThreadParams* params = new ThreadParams; - params->delegate = delegate; - - // Using CreateThread here vs _beginthreadex makes thread creation a bit - // faster and doesn't require the loader lock to be available. Our code will - // have to work running on CreateThread() threads anyway, since we run code on - // the Windows thread pool, etc. For some background on the difference: - // http://www.microsoft.com/msj/1099/win32/win321099.aspx - void* thread_handle = - ::CreateThread(nullptr, stack_size, ThreadFunc, params, flags, nullptr); - - if (!thread_handle) { - DWORD last_error = ::GetLastError(); - - switch (last_error) { - case ERROR_NOT_ENOUGH_MEMORY: - case ERROR_OUTOFMEMORY: - case ERROR_COMMITMENT_LIMIT: - TerminateBecauseOutOfMemory(stack_size); - break; - - default: - break; - } - - delete params; - return false; - } - - if (out_thread_handle) - *out_thread_handle = PlatformThreadHandle(thread_handle); - else - CloseHandle(thread_handle); - return true; -} - -} // namespace - -// static -void PlatformThreadForTesting::YieldCurrentThread() { - ::Sleep(0); -} - -// static -void PlatformThreadForTesting::Join(PlatformThreadHandle thread_handle) { - PA_DCHECK(thread_handle.platform_handle()); - - DWORD thread_id = 0; - thread_id = ::GetThreadId(thread_handle.platform_handle()); - DWORD last_error = 0; - if (!thread_id) - last_error = ::GetLastError(); - - // Record information about the exiting thread in case joining hangs. - base::debug::Alias(&thread_id); - base::debug::Alias(&last_error); - - // Remove ScopedBlockingCallWithBaseSyncPrimitives, because only partition - // alloc tests use PlatformThread::Join. So there is no special requirement - // to monitor blocking calls - // (by using ThreadGroupImpl::WorkerThreadDelegateImpl). - // - // base::internal::ScopedBlockingCallWithBaseSyncPrimitives - // scoped_blocking_call(base::BlockingType::MAY_BLOCK); - - // Wait for the thread to exit. It should already have terminated but make - // sure this assumption is valid. - PA_CHECK(WAIT_OBJECT_0 == - WaitForSingleObject(thread_handle.platform_handle(), INFINITE)); - CloseHandle(thread_handle.platform_handle()); -} - -// static -bool PlatformThreadForTesting::Create(size_t stack_size, - Delegate* delegate, - PlatformThreadHandle* thread_handle) { - PA_DCHECK(thread_handle); - return CreateThreadInternal(stack_size, delegate, thread_handle); -} - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/time/time.cc b/base/allocator/partition_allocator/partition_alloc_base/time/time.cc deleted file mode 100644 index 1683ab3..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/time/time.cc +++ /dev/null
@@ -1,263 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/time/time.h" - -#include <atomic> -#include <cmath> -#include <limits> -#include <ostream> -#include <tuple> -#include <utility> - -#include "base/allocator/partition_allocator/partition_alloc_base/time/time_override.h" - -namespace partition_alloc::internal::base { - -namespace internal { - -std::atomic<TimeNowFunction> g_time_now_function{ - &subtle::TimeNowIgnoringOverride}; - -std::atomic<TimeNowFunction> g_time_now_from_system_time_function{ - &subtle::TimeNowFromSystemTimeIgnoringOverride}; - -std::atomic<TimeTicksNowFunction> g_time_ticks_now_function{ - &subtle::TimeTicksNowIgnoringOverride}; - -std::atomic<ThreadTicksNowFunction> g_thread_ticks_now_function{ - &subtle::ThreadTicksNowIgnoringOverride}; - -} // namespace internal - -// TimeDelta ------------------------------------------------------------------ - -int TimeDelta::InDays() const { - if (!is_inf()) - return static_cast<int>(delta_ / Time::kMicrosecondsPerDay); - return (delta_ < 0) ? std::numeric_limits<int>::min() - : std::numeric_limits<int>::max(); -} - -int TimeDelta::InDaysFloored() const { - if (!is_inf()) { - const int result = delta_ / Time::kMicrosecondsPerDay; - // Convert |result| from truncating to flooring. - return (result * Time::kMicrosecondsPerDay > delta_) ? (result - 1) - : result; - } - return (delta_ < 0) ? std::numeric_limits<int>::min() - : std::numeric_limits<int>::max(); -} - -double TimeDelta::InMillisecondsF() const { - if (!is_inf()) - return static_cast<double>(delta_) / Time::kMicrosecondsPerMillisecond; - return (delta_ < 0) ? -std::numeric_limits<double>::infinity() - : std::numeric_limits<double>::infinity(); -} - -int64_t TimeDelta::InMilliseconds() const { - if (!is_inf()) - return delta_ / Time::kMicrosecondsPerMillisecond; - return (delta_ < 0) ? std::numeric_limits<int64_t>::min() - : std::numeric_limits<int64_t>::max(); -} - -int64_t TimeDelta::InMillisecondsRoundedUp() const { - if (!is_inf()) { - const int64_t result = delta_ / Time::kMicrosecondsPerMillisecond; - // Convert |result| from truncating to ceiling. - return (delta_ > result * Time::kMicrosecondsPerMillisecond) ? (result + 1) - : result; - } - return delta_; -} - -double TimeDelta::InMicrosecondsF() const { - if (!is_inf()) - return static_cast<double>(delta_); - return (delta_ < 0) ? -std::numeric_limits<double>::infinity() - : std::numeric_limits<double>::infinity(); -} - -TimeDelta TimeDelta::CeilToMultiple(TimeDelta interval) const { - if (is_inf() || interval.is_zero()) - return *this; - const TimeDelta remainder = *this % interval; - if (delta_ < 0) - return *this - remainder; - return remainder.is_zero() ? *this - : (*this - remainder + interval.magnitude()); -} - -TimeDelta TimeDelta::FloorToMultiple(TimeDelta interval) const { - if (is_inf() || interval.is_zero()) - return *this; - const TimeDelta remainder = *this % interval; - if (delta_ < 0) { - return remainder.is_zero() ? *this - : (*this - remainder - interval.magnitude()); - } - return *this - remainder; -} - -TimeDelta TimeDelta::RoundToMultiple(TimeDelta interval) const { - if (is_inf() || interval.is_zero()) - return *this; - if (interval.is_inf()) - return TimeDelta(); - const TimeDelta half = interval.magnitude() / 2; - return (delta_ < 0) ? (*this - half).CeilToMultiple(interval) - : (*this + half).FloorToMultiple(interval); -} - -// Time ----------------------------------------------------------------------- - -// static -Time Time::Now() { - return internal::g_time_now_function.load(std::memory_order_relaxed)(); -} - -// static -Time Time::NowFromSystemTime() { - // Just use g_time_now_function because it returns the system time. - return internal::g_time_now_from_system_time_function.load( - std::memory_order_relaxed)(); -} - -time_t Time::ToTimeT() const { - if (is_null()) - return 0; // Preserve 0 so we can tell it doesn't exist. - if (!is_inf() && ((std::numeric_limits<int64_t>::max() - - kTimeTToMicrosecondsOffset) > us_)) - return (*this - UnixEpoch()).InSeconds(); - return (us_ < 0) ? std::numeric_limits<time_t>::min() - : std::numeric_limits<time_t>::max(); -} - -// static -Time Time::FromDoubleT(double dt) { - // Preserve 0 so we can tell it doesn't exist. - return (dt == 0 || std::isnan(dt)) ? Time() : (UnixEpoch() + Seconds(dt)); -} - -double Time::ToDoubleT() const { - if (is_null()) - return 0; // Preserve 0 so we can tell it doesn't exist. - if (!is_inf()) - return (*this - UnixEpoch()).InSecondsF(); - return (us_ < 0) ? -std::numeric_limits<double>::infinity() - : std::numeric_limits<double>::infinity(); -} - -#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) -// static -Time Time::FromTimeSpec(const timespec& ts) { - return FromDoubleT(ts.tv_sec + - static_cast<double>(ts.tv_nsec) / kNanosecondsPerSecond); -} -#endif - -// static -Time Time::FromJsTime(double ms_since_epoch) { - // The epoch is a valid time, so this constructor doesn't interpret 0 as the - // null time. - return UnixEpoch() + Milliseconds(ms_since_epoch); -} - -double Time::ToJsTime() const { - // Preserve 0 so the invalid result doesn't depend on the platform. - return is_null() ? 0 : ToJsTimeIgnoringNull(); -} - -double Time::ToJsTimeIgnoringNull() const { - // Preserve max and min without offset to prevent over/underflow. - if (!is_inf()) - return (*this - UnixEpoch()).InMillisecondsF(); - return (us_ < 0) ? -std::numeric_limits<double>::infinity() - : std::numeric_limits<double>::infinity(); -} - -Time Time::FromJavaTime(int64_t ms_since_epoch) { - return UnixEpoch() + Milliseconds(ms_since_epoch); -} - -int64_t Time::ToJavaTime() const { - // Preserve 0 so the invalid result doesn't depend on the platform. - if (is_null()) - return 0; - if (!is_inf()) - return (*this - UnixEpoch()).InMilliseconds(); - return (us_ < 0) ? std::numeric_limits<int64_t>::min() - : std::numeric_limits<int64_t>::max(); -} - -// static -bool Time::FromMillisecondsSinceUnixEpoch(int64_t unix_milliseconds, - Time* time) { - // Adjust the provided time from milliseconds since the Unix epoch (1970) to - // microseconds since the Windows epoch (1601), avoiding overflows. - CheckedNumeric<int64_t> checked_microseconds_win_epoch = unix_milliseconds; - checked_microseconds_win_epoch *= kMicrosecondsPerMillisecond; - checked_microseconds_win_epoch += kTimeTToMicrosecondsOffset; - *time = Time(checked_microseconds_win_epoch.ValueOrDefault(0)); - return checked_microseconds_win_epoch.IsValid(); -} - -int64_t Time::ToRoundedDownMillisecondsSinceUnixEpoch() const { - constexpr int64_t kEpochOffsetMillis = - kTimeTToMicrosecondsOffset / kMicrosecondsPerMillisecond; - static_assert(kTimeTToMicrosecondsOffset % kMicrosecondsPerMillisecond == 0, - "assumption: no epoch offset sub-milliseconds"); - - // Compute the milliseconds since UNIX epoch without the possibility of - // under/overflow. Round the result towards -infinity. - // - // If |us_| is negative and includes fractions of a millisecond, subtract one - // more to effect the round towards -infinity. C-style integer truncation - // takes care of all other cases. - const int64_t millis = us_ / kMicrosecondsPerMillisecond; - const int64_t submillis = us_ % kMicrosecondsPerMillisecond; - return millis - kEpochOffsetMillis - (submillis < 0); -} - -// TimeTicks ------------------------------------------------------------------ - -// static -TimeTicks TimeTicks::Now() { - return internal::g_time_ticks_now_function.load(std::memory_order_relaxed)(); -} - -// static -TimeTicks TimeTicks::UnixEpoch() { - static const TimeTicks epoch([]() { - return subtle::TimeTicksNowIgnoringOverride() - - (subtle::TimeNowIgnoringOverride() - Time::UnixEpoch()); - }()); - return epoch; -} - -TimeTicks TimeTicks::SnappedToNextTick(TimeTicks tick_phase, - TimeDelta tick_interval) const { - // |interval_offset| is the offset from |this| to the next multiple of - // |tick_interval| after |tick_phase|, possibly negative if in the past. - TimeDelta interval_offset = (tick_phase - *this) % tick_interval; - // If |this| is exactly on the interval (i.e. offset==0), don't adjust. - // Otherwise, if |tick_phase| was in the past, adjust forward to the next - // tick after |this|. - if (!interval_offset.is_zero() && tick_phase < *this) - interval_offset += tick_interval; - return *this + interval_offset; -} - -// ThreadTicks ---------------------------------------------------------------- - -// static -ThreadTicks ThreadTicks::Now() { - return internal::g_thread_ticks_now_function.load( - std::memory_order_relaxed)(); -} - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/time/time.h b/base/allocator/partition_allocator/partition_alloc_base/time/time.h deleted file mode 100644 index 82a8d6b..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/time/time.h +++ /dev/null
@@ -1,1050 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// `Time` represents an absolute point in coordinated universal time (UTC), -// internally represented as microseconds (s/1,000,000) since the Windows epoch -// (1601-01-01 00:00:00 UTC). System-dependent clock interface routines are -// defined in time_PLATFORM.cc. Note that values for `Time` may skew and jump -// around as the operating system makes adjustments to synchronize (e.g., with -// NTP servers). Thus, client code that uses the `Time` class must account for -// this. -// -// `TimeDelta` represents a duration of time, internally represented in -// microseconds. -// -// `TimeTicks` and `ThreadTicks` represent an abstract time that is most of the -// time incrementing, for use in measuring time durations. Internally, they are -// represented in microseconds. They cannot be converted to a human-readable -// time, but are guaranteed not to decrease (unlike the `Time` class). Note -// that `TimeTicks` may "stand still" (e.g., if the computer is suspended), and -// `ThreadTicks` will "stand still" whenever the thread has been de-scheduled -// by the operating system. -// -// All time classes are copyable, assignable, and occupy 64 bits per instance. -// Prefer to pass them by value, e.g.: -// -// void MyFunction(TimeDelta arg); -// -// All time classes support `operator<<` with logging streams, e.g. `LOG(INFO)`. -// For human-readable formatting, use //base/i18n/time_formatting.h. -// -// Example use cases for different time classes: -// -// Time: Interpreting the wall-clock time provided by a remote system. -// Detecting whether cached resources have expired. Providing the -// user with a display of the current date and time. Determining -// the amount of time between events across re-boots of the -// machine. -// -// TimeTicks: Tracking the amount of time a task runs. Executing delayed -// tasks at the right time. Computing presentation timestamps. -// Synchronizing audio and video using TimeTicks as a common -// reference clock (lip-sync). Measuring network round-trip -// latency. -// -// ThreadTicks: Benchmarking how long the current thread has been doing actual -// work. -// -// Serialization: -// -// Use the helpers in //base/json/values_util.h when serializing `Time` -// or `TimeDelta` to/from `base::Value`. -// -// Otherwise: -// -// - Time: use `FromDeltaSinceWindowsEpoch()`/`ToDeltaSinceWindowsEpoch()`. -// - TimeDelta: use `base::Microseconds()`/`InMicroseconds()`. -// -// `TimeTicks` and `ThreadTicks` do not have a stable origin; serialization for -// the purpose of persistence is not supported. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_TIME_TIME_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_TIME_TIME_H_ - -#include <stdint.h> -#include <time.h> - -#include <iosfwd> -#include <limits> - -#include "base/allocator/partition_allocator/chromeos_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_base/numerics/clamped_math.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "build/build_config.h" - -#if BUILDFLAG(IS_APPLE) -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#endif // BUILDFLAG(IS_APPLE) - -#if BUILDFLAG(IS_FUCHSIA) -#include <zircon/types.h> -#endif - -#if BUILDFLAG(IS_APPLE) -#include <CoreFoundation/CoreFoundation.h> -#include <mach/mach_time.h> -// Avoid Mac system header macro leak. -#undef TYPE_BOOL -#endif - -#if BUILDFLAG(IS_ANDROID) -#include <jni.h> -#endif - -#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) -#include <sys/time.h> -#include <unistd.h> -#endif - -#if BUILDFLAG(IS_WIN) -#include "base/allocator/partition_allocator/partition_alloc_base/win/windows_types.h" - -namespace ABI { -namespace Windows { -namespace Foundation { -struct DateTime; -} // namespace Foundation -} // namespace Windows -} // namespace ABI -#endif - -namespace partition_alloc::internal::base { - -class TimeDelta; - -template <typename T> -constexpr TimeDelta Microseconds(T n); - -#if BUILDFLAG(IS_WIN) -class PlatformThreadHandle; -#endif - -// TimeDelta ------------------------------------------------------------------ - -class PA_COMPONENT_EXPORT(PARTITION_ALLOC) TimeDelta { - public: - constexpr TimeDelta() = default; - -#if BUILDFLAG(IS_WIN) - static TimeDelta FromQPCValue(LONGLONG qpc_value); - // TODO(crbug.com/989694): Avoid base::TimeDelta factory functions - // based on absolute time - static TimeDelta FromFileTime(FILETIME ft); - static TimeDelta FromWinrtDateTime(ABI::Windows::Foundation::DateTime dt); -#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) - static TimeDelta FromTimeSpec(const timespec& ts); -#endif -#if BUILDFLAG(IS_FUCHSIA) - static TimeDelta FromZxDuration(zx_duration_t nanos); -#endif -#if BUILDFLAG(IS_APPLE) -#if BUILDFLAG(PARTITION_ALLOC_ENABLE_MACH_ABSOLUTE_TIME_TICKS) - static TimeDelta FromMachTime(uint64_t mach_time); -#endif // BUILDFLAG(PARTITION_ALLOC_ENABLE_MACH_ABSOLUTE_TIME_TICKS) -#endif // BUILDFLAG(IS_APPLE) - - // Converts an integer value representing TimeDelta to a class. This is used - // when deserializing a |TimeDelta| structure, using a value known to be - // compatible. It is not provided as a constructor because the integer type - // may be unclear from the perspective of a caller. - // - // DEPRECATED - Do not use in new code. http://crbug.com/634507 - static constexpr TimeDelta FromInternalValue(int64_t delta) { - return TimeDelta(delta); - } - - // Returns the maximum time delta, which should be greater than any reasonable - // time delta we might compare it to. If converted to double with ToDouble() - // it becomes an IEEE double infinity. Use FiniteMax() if you want a very - // large number that doesn't do this. TimeDelta math saturates at the end - // points so adding to TimeDelta::Max() leaves the value unchanged. - // Subtracting should leave the value unchanged but currently changes it - // TODO(https://crbug.com/869387). - static constexpr TimeDelta Max(); - - // Returns the minimum time delta, which should be less than than any - // reasonable time delta we might compare it to. For more details see the - // comments for Max(). - static constexpr TimeDelta Min(); - - // Returns the maximum time delta which is not equivalent to infinity. Only - // subtracting a finite time delta from this time delta has a defined result. - static constexpr TimeDelta FiniteMax(); - - // Returns the minimum time delta which is not equivalent to -infinity. Only - // adding a finite time delta to this time delta has a defined result. - static constexpr TimeDelta FiniteMin(); - - // Returns the internal numeric value of the TimeDelta object. Please don't - // use this and do arithmetic on it, as it is more error prone than using the - // provided operators. - // For serializing, use FromInternalValue to reconstitute. - // - // DEPRECATED - Do not use in new code. http://crbug.com/634507 - constexpr int64_t ToInternalValue() const { return delta_; } - - // Returns the magnitude (absolute value) of this TimeDelta. - constexpr TimeDelta magnitude() const { return TimeDelta(delta_.Abs()); } - - // Returns true if the time delta is a zero, positive or negative time delta. - constexpr bool is_zero() const { return delta_ == 0; } - constexpr bool is_positive() const { return delta_ > 0; } - constexpr bool is_negative() const { return delta_ < 0; } - - // Returns true if the time delta is the maximum/minimum time delta. - constexpr bool is_max() const { return *this == Max(); } - constexpr bool is_min() const { return *this == Min(); } - constexpr bool is_inf() const { return is_min() || is_max(); } - -#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) - struct timespec ToTimeSpec() const; -#endif -#if BUILDFLAG(IS_FUCHSIA) - zx_duration_t ToZxDuration() const; -#endif -#if BUILDFLAG(IS_WIN) - ABI::Windows::Foundation::DateTime ToWinrtDateTime() const; -#endif - - // Returns the frequency in Hertz (cycles per second) that has a period of - // *this. - constexpr double ToHz() const; - - // Returns the time delta in some unit. Minimum argument values return as - // -inf for doubles and min type values otherwise. Maximum ones are treated as - // +inf for doubles and max type values otherwise. Their results will produce - // an is_min() or is_max() TimeDelta. The InXYZF versions return a floating - // point value. The InXYZ versions return a truncated value (aka rounded - // towards zero, std::trunc() behavior). The InXYZFloored() versions round to - // lesser integers (std::floor() behavior). The XYZRoundedUp() versions round - // up to greater integers (std::ceil() behavior). WARNING: Floating point - // arithmetic is such that XXX(t.InXXXF()) may not precisely equal |t|. - // Hence, floating point values should not be used for storage. - int InDays() const; - int InDaysFloored() const; - constexpr int InHours() const; - constexpr int InMinutes() const; - constexpr double InSecondsF() const; - constexpr int64_t InSeconds() const; - double InMillisecondsF() const; - int64_t InMilliseconds() const; - int64_t InMillisecondsRoundedUp() const; - constexpr int64_t InMicroseconds() const { return delta_; } - double InMicrosecondsF() const; - constexpr int64_t InNanoseconds() const; - - // Computations with other deltas. - constexpr TimeDelta operator+(TimeDelta other) const; - constexpr TimeDelta operator-(TimeDelta other) const; - - constexpr TimeDelta& operator+=(TimeDelta other) { - return *this = (*this + other); - } - constexpr TimeDelta& operator-=(TimeDelta other) { - return *this = (*this - other); - } - constexpr TimeDelta operator-() const { - if (!is_inf()) - return TimeDelta(-delta_); - return (delta_ < 0) ? Max() : Min(); - } - - // Computations with numeric types. - template <typename T> - constexpr TimeDelta operator*(T a) const { - return TimeDelta(int64_t{delta_ * a}); - } - template <typename T> - constexpr TimeDelta operator/(T a) const { - return TimeDelta(int64_t{delta_ / a}); - } - template <typename T> - constexpr TimeDelta& operator*=(T a) { - return *this = (*this * a); - } - template <typename T> - constexpr TimeDelta& operator/=(T a) { - return *this = (*this / a); - } - - // This does floating-point division. For an integer result, either call - // IntDiv(), or (possibly clearer) use this operator with - // base::Clamp{Ceil,Floor,Round}() or base::saturated_cast() (for truncation). - // Note that converting to double here drops precision to 53 bits. - constexpr double operator/(TimeDelta a) const { - // 0/0 and inf/inf (any combination of positive and negative) are invalid - // (they are almost certainly not intentional, and result in NaN, which - // turns into 0 if clamped to an integer; this makes introducing subtle bugs - // too easy). - PA_CHECK(!is_zero() || !a.is_zero()); - PA_CHECK(!is_inf() || !a.is_inf()); - - return ToDouble() / a.ToDouble(); - } - constexpr int64_t IntDiv(TimeDelta a) const { - if (!is_inf() && !a.is_zero()) - return int64_t{delta_ / a.delta_}; - - // For consistency, use the same edge case CHECKs and behavior as the code - // above. - PA_CHECK(!is_zero() || !a.is_zero()); - PA_CHECK(!is_inf() || !a.is_inf()); - return ((delta_ < 0) == (a.delta_ < 0)) - ? std::numeric_limits<int64_t>::max() - : std::numeric_limits<int64_t>::min(); - } - - constexpr TimeDelta operator%(TimeDelta a) const { - return TimeDelta( - (is_inf() || a.is_zero() || a.is_inf()) ? delta_ : (delta_ % a.delta_)); - } - constexpr TimeDelta& operator%=(TimeDelta other) { - return *this = (*this % other); - } - - // Comparison operators. - constexpr bool operator==(TimeDelta other) const { - return delta_ == other.delta_; - } - constexpr bool operator!=(TimeDelta other) const { - return delta_ != other.delta_; - } - constexpr bool operator<(TimeDelta other) const { - return delta_ < other.delta_; - } - constexpr bool operator<=(TimeDelta other) const { - return delta_ <= other.delta_; - } - constexpr bool operator>(TimeDelta other) const { - return delta_ > other.delta_; - } - constexpr bool operator>=(TimeDelta other) const { - return delta_ >= other.delta_; - } - - // Returns this delta, ceiled/floored/rounded-away-from-zero to the nearest - // multiple of |interval|. - TimeDelta CeilToMultiple(TimeDelta interval) const; - TimeDelta FloorToMultiple(TimeDelta interval) const; - TimeDelta RoundToMultiple(TimeDelta interval) const; - - private: - // Constructs a delta given the duration in microseconds. This is private - // to avoid confusion by callers with an integer constructor. Use - // base::Seconds, base::Milliseconds, etc. instead. - constexpr explicit TimeDelta(int64_t delta_us) : delta_(delta_us) {} - constexpr explicit TimeDelta(ClampedNumeric<int64_t> delta_us) - : delta_(delta_us) {} - - // Returns a double representation of this TimeDelta's tick count. In - // particular, Max()/Min() are converted to +/-infinity. - constexpr double ToDouble() const { - if (!is_inf()) - return static_cast<double>(delta_); - return (delta_ < 0) ? -std::numeric_limits<double>::infinity() - : std::numeric_limits<double>::infinity(); - } - - // Delta in microseconds. - ClampedNumeric<int64_t> delta_ = 0; -}; - -constexpr TimeDelta TimeDelta::operator+(TimeDelta other) const { - if (!other.is_inf()) - return TimeDelta(delta_ + other.delta_); - - // Additions involving two infinities are only valid if signs match. - PA_CHECK(!is_inf() || (delta_ == other.delta_)); - return other; -} - -constexpr TimeDelta TimeDelta::operator-(TimeDelta other) const { - if (!other.is_inf()) - return TimeDelta(delta_ - other.delta_); - - // Subtractions involving two infinities are only valid if signs differ. - PA_CHECK(int64_t{delta_} != int64_t{other.delta_}); - return (other.delta_ < 0) ? Max() : Min(); -} - -template <typename T> -constexpr TimeDelta operator*(T a, TimeDelta td) { - return td * a; -} - -// TimeBase-------------------------------------------------------------------- - -// Do not reference the time_internal::TimeBase template class directly. Please -// use one of the time subclasses instead, and only reference the public -// TimeBase members via those classes. -namespace time_internal { - -// Provides value storage and comparison/math operations common to all time -// classes. Each subclass provides for strong type-checking to ensure -// semantically meaningful comparison/math of time values from the same clock -// source or timeline. -template <class TimeClass> -class TimeBase { - public: - static constexpr int64_t kHoursPerDay = 24; - static constexpr int64_t kSecondsPerMinute = 60; - static constexpr int64_t kMinutesPerHour = 60; - static constexpr int64_t kSecondsPerHour = - kSecondsPerMinute * kMinutesPerHour; - static constexpr int64_t kMillisecondsPerSecond = 1000; - static constexpr int64_t kMillisecondsPerDay = - kMillisecondsPerSecond * kSecondsPerHour * kHoursPerDay; - static constexpr int64_t kMicrosecondsPerMillisecond = 1000; - static constexpr int64_t kMicrosecondsPerSecond = - kMicrosecondsPerMillisecond * kMillisecondsPerSecond; - static constexpr int64_t kMicrosecondsPerMinute = - kMicrosecondsPerSecond * kSecondsPerMinute; - static constexpr int64_t kMicrosecondsPerHour = - kMicrosecondsPerMinute * kMinutesPerHour; - static constexpr int64_t kMicrosecondsPerDay = - kMicrosecondsPerHour * kHoursPerDay; - static constexpr int64_t kMicrosecondsPerWeek = kMicrosecondsPerDay * 7; - static constexpr int64_t kNanosecondsPerMicrosecond = 1000; - static constexpr int64_t kNanosecondsPerSecond = - kNanosecondsPerMicrosecond * kMicrosecondsPerSecond; - - // Returns true if this object has not been initialized. - // - // Warning: Be careful when writing code that performs math on time values, - // since it's possible to produce a valid "zero" result that should not be - // interpreted as a "null" value. - constexpr bool is_null() const { return us_ == 0; } - - // Returns true if this object represents the maximum/minimum time. - constexpr bool is_max() const { return *this == Max(); } - constexpr bool is_min() const { return *this == Min(); } - constexpr bool is_inf() const { return is_min() || is_max(); } - - // Returns the maximum/minimum times, which should be greater/less than than - // any reasonable time with which we might compare it. - static constexpr TimeClass Max() { - return TimeClass(std::numeric_limits<int64_t>::max()); - } - - static constexpr TimeClass Min() { - return TimeClass(std::numeric_limits<int64_t>::min()); - } - - // For legacy serialization only. When serializing to `base::Value`, prefer - // the helpers from //base/json/values_util.h instead. Otherwise, use - // `Time::ToDeltaSinceWindowsEpoch()` for `Time` and - // `TimeDelta::InMiseconds()` for `TimeDelta`. See http://crbug.com/634507. - constexpr int64_t ToInternalValue() const { return us_; } - - // The amount of time since the origin (or "zero") point. This is a syntactic - // convenience to aid in code readability, mainly for debugging/testing use - // cases. - // - // Warning: While the Time subclass has a fixed origin point, the origin for - // the other subclasses can vary each time the application is restarted. - constexpr TimeDelta since_origin() const; - - // Compute the difference between two times. - constexpr TimeDelta operator-(const TimeBase<TimeClass>& other) const; - - // Return a new time modified by some delta. - constexpr TimeClass operator+(TimeDelta delta) const; - constexpr TimeClass operator-(TimeDelta delta) const; - - // Modify by some time delta. - constexpr TimeClass& operator+=(TimeDelta delta) { - return static_cast<TimeClass&>(*this = (*this + delta)); - } - constexpr TimeClass& operator-=(TimeDelta delta) { - return static_cast<TimeClass&>(*this = (*this - delta)); - } - - // Comparison operators - constexpr bool operator==(const TimeBase<TimeClass>& other) const { - return us_ == other.us_; - } - constexpr bool operator!=(const TimeBase<TimeClass>& other) const { - return us_ != other.us_; - } - constexpr bool operator<(const TimeBase<TimeClass>& other) const { - return us_ < other.us_; - } - constexpr bool operator<=(const TimeBase<TimeClass>& other) const { - return us_ <= other.us_; - } - constexpr bool operator>(const TimeBase<TimeClass>& other) const { - return us_ > other.us_; - } - constexpr bool operator>=(const TimeBase<TimeClass>& other) const { - return us_ >= other.us_; - } - - protected: - constexpr explicit TimeBase(int64_t us) : us_(us) {} - - // Time value in a microsecond timebase. - int64_t us_; -}; - -#if BUILDFLAG(IS_WIN) -#if defined(ARCH_CPU_ARM64) -// TSCTicksPerSecond is not supported on Windows on Arm systems because the -// cycle-counting methods use the actual CPU cycle count, and not a consistent -// incrementing counter. -#else -// Returns true if the CPU support constant rate TSC. -[[nodiscard]] PA_COMPONENT_EXPORT(PARTITION_ALLOC) bool HasConstantRateTSC(); - -// Returns the frequency of the TSC in ticks per second, or 0 if it hasn't -// been measured yet. Needs to be guarded with a call to HasConstantRateTSC(). -[[nodiscard]] PA_COMPONENT_EXPORT(PARTITION_ALLOC) double TSCTicksPerSecond(); -#endif -#endif // BUILDFLAG(IS_WIN) - -} // namespace time_internal - -template <class TimeClass> -inline constexpr TimeClass operator+(TimeDelta delta, TimeClass t) { - return t + delta; -} - -// Time ----------------------------------------------------------------------- - -// Represents a wall clock time in UTC. Values are not guaranteed to be -// monotonically non-decreasing and are subject to large amounts of skew. -// Time is stored internally as microseconds since the Windows epoch (1601). -class PA_COMPONENT_EXPORT(PARTITION_ALLOC) Time - : public time_internal::TimeBase<Time> { - public: - // Offset of UNIX epoch (1970-01-01 00:00:00 UTC) from Windows FILETIME epoch - // (1601-01-01 00:00:00 UTC), in microseconds. This value is derived from the - // following: ((1970-1601)*365+89)*24*60*60*1000*1000, where 89 is the number - // of leap year days between 1601 and 1970: (1970-1601)/4 excluding 1700, - // 1800, and 1900. - static constexpr int64_t kTimeTToMicrosecondsOffset = - INT64_C(11644473600000000); - -#if BUILDFLAG(IS_WIN) - // To avoid overflow in QPC to Microseconds calculations, since we multiply - // by kMicrosecondsPerSecond, then the QPC value should not exceed - // (2^63 - 1) / 1E6. If it exceeds that threshold, we divide then multiply. - static constexpr int64_t kQPCOverflowThreshold = INT64_C(0x8637BD05AF7); -#endif - - // Contains the NULL time. Use Time::Now() to get the current time. - constexpr Time() : TimeBase(0) {} - - // Returns the time for epoch in Unix-like system (Jan 1, 1970). - static constexpr Time UnixEpoch() { return Time(kTimeTToMicrosecondsOffset); } - - // Returns the current time. Watch out, the system might adjust its clock - // in which case time will actually go backwards. We don't guarantee that - // times are increasing, or that two calls to Now() won't be the same. - static Time Now(); - - // Returns the current time. Same as Now() except that this function always - // uses system time so that there are no discrepancies between the returned - // time and system time even on virtual environments including our test bot. - // For timing sensitive unittests, this function should be used. - static Time NowFromSystemTime(); - - // Converts to/from TimeDeltas relative to the Windows epoch (1601-01-01 - // 00:00:00 UTC). - // - // For serialization, when handling `base::Value`, prefer the helpers in - // //base/json/values_util.h instead. Otherwise, use these methods for - // opaque serialization and deserialization, e.g. - // - // // Serialization: - // base::Time last_updated = ...; - // SaveToDatabase(last_updated.ToDeltaSinceWindowsEpoch().InMicroseconds()); - // - // // Deserialization: - // base::Time last_updated = base::Time::FromDeltaSinceWindowsEpoch( - // base::Microseconds(LoadFromDatabase())); - // - // Do not use `FromInternalValue()` or `ToInternalValue()` for this purpose. - static constexpr Time FromDeltaSinceWindowsEpoch(TimeDelta delta) { - return Time(delta.InMicroseconds()); - } - - constexpr TimeDelta ToDeltaSinceWindowsEpoch() const { - return Microseconds(us_); - } - - // Converts to/from time_t in UTC and a Time class. - static constexpr Time FromTimeT(time_t tt); - time_t ToTimeT() const; - - // Converts time to/from a double which is the number of seconds since epoch - // (Jan 1, 1970). Webkit uses this format to represent time. - // Because WebKit initializes double time value to 0 to indicate "not - // initialized", we map it to empty Time object that also means "not - // initialized". - static Time FromDoubleT(double dt); - double ToDoubleT() const; - -#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) - // Converts the timespec structure to time. MacOS X 10.8.3 (and tentatively, - // earlier versions) will have the |ts|'s tv_nsec component zeroed out, - // having a 1 second resolution, which agrees with - // https://developer.apple.com/legacy/library/#technotes/tn/tn1150.html#HFSPlusDates. - static Time FromTimeSpec(const timespec& ts); -#endif - - // Converts to/from the Javascript convention for times, a number of - // milliseconds since the epoch: - // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date/getTime. - // - // Don't use ToJsTime() in new code, since it contains a subtle hack (only - // exactly 1601-01-01 00:00 UTC is represented as 1970-01-01 00:00 UTC), and - // that is not appropriate for general use. Try to use ToJsTimeIgnoringNull() - // unless you have a very good reason to use ToJsTime(). - static Time FromJsTime(double ms_since_epoch); - double ToJsTime() const; - double ToJsTimeIgnoringNull() const; - - // Converts to/from Java convention for times, a number of milliseconds since - // the epoch. Because the Java format has less resolution, converting to Java - // time is a lossy operation. - static Time FromJavaTime(int64_t ms_since_epoch); - int64_t ToJavaTime() const; - -#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) - static Time FromTimeVal(struct timeval t); - struct timeval ToTimeVal() const; -#endif - -#if BUILDFLAG(IS_FUCHSIA) - static Time FromZxTime(zx_time_t time); - zx_time_t ToZxTime() const; -#endif - -#if BUILDFLAG(IS_APPLE) - static Time FromCFAbsoluteTime(CFAbsoluteTime t); - CFAbsoluteTime ToCFAbsoluteTime() const; -#if defined(__OBJC__) - static Time FromNSDate(NSDate* date); - NSDate* ToNSDate() const; -#endif -#endif - -#if BUILDFLAG(IS_WIN) - static Time FromFileTime(FILETIME ft); - FILETIME ToFileTime() const; - - // The minimum time of a low resolution timer. This is basically a windows - // constant of ~15.6ms. While it does vary on some older OS versions, we'll - // treat it as static across all windows versions. - static const int kMinLowResolutionThresholdMs = 16; - - // Enable or disable Windows high resolution timer. - static void EnableHighResolutionTimer(bool enable); - - // Activates or deactivates the high resolution timer based on the |activate| - // flag. If the HighResolutionTimer is not Enabled (see - // EnableHighResolutionTimer), this function will return false. Otherwise - // returns true. Each successful activate call must be paired with a - // subsequent deactivate call. - // All callers to activate the high resolution timer must eventually call - // this function to deactivate the high resolution timer. - static bool ActivateHighResolutionTimer(bool activate); - - // Returns true if the high resolution timer is both enabled and activated. - // This is provided for testing only, and is not tracked in a thread-safe - // way. - static bool IsHighResolutionTimerInUse(); - - // The following two functions are used to report the fraction of elapsed time - // that the high resolution timer is activated. - // ResetHighResolutionTimerUsage() resets the cumulative usage and starts the - // measurement interval and GetHighResolutionTimerUsage() returns the - // percentage of time since the reset that the high resolution timer was - // activated. - // ResetHighResolutionTimerUsage() must be called at least once before calling - // GetHighResolutionTimerUsage(); otherwise the usage result would be - // undefined. - static void ResetHighResolutionTimerUsage(); - static double GetHighResolutionTimerUsage(); -#endif // BUILDFLAG(IS_WIN) - - // For legacy deserialization only. Converts an integer value representing - // Time to a class. This may be used when deserializing a |Time| structure, - // using a value known to be compatible. It is not provided as a constructor - // because the integer type may be unclear from the perspective of a caller. - // - // DEPRECATED - Do not use in new code. When deserializing from `base::Value`, - // prefer the helpers from //base/json/values_util.h instead. - // Otherwise, use `Time::FromDeltaSinceWindowsEpoch()` for `Time` and - // `TimeDelta::FromMiseconds()` for `TimeDelta`. http://crbug.com/634507 - static constexpr Time FromInternalValue(int64_t us) { return Time(us); } - - private: - friend class time_internal::TimeBase<Time>; - - constexpr explicit Time(int64_t microseconds_since_win_epoch) - : TimeBase(microseconds_since_win_epoch) {} - - // Converts the provided time in milliseconds since the Unix epoch (1970) to a - // Time object, avoiding overflows. - [[nodiscard]] static bool FromMillisecondsSinceUnixEpoch( - int64_t unix_milliseconds, - Time* time); - - // Returns the milliseconds since the Unix epoch (1970), rounding the - // microseconds towards -infinity. - int64_t ToRoundedDownMillisecondsSinceUnixEpoch() const; -}; - -// Factory methods that return a TimeDelta of the given unit. -// WARNING: Floating point arithmetic is such that XXX(t.InXXXF()) may not -// precisely equal |t|. Hence, floating point values should not be used for -// storage. - -template <typename T> -constexpr TimeDelta Days(T n) { - return TimeDelta::FromInternalValue(MakeClampedNum(n) * - Time::kMicrosecondsPerDay); -} -template <typename T> -constexpr TimeDelta Hours(T n) { - return TimeDelta::FromInternalValue(MakeClampedNum(n) * - Time::kMicrosecondsPerHour); -} -template <typename T> -constexpr TimeDelta Minutes(T n) { - return TimeDelta::FromInternalValue(MakeClampedNum(n) * - Time::kMicrosecondsPerMinute); -} -template <typename T> -constexpr TimeDelta Seconds(T n) { - return TimeDelta::FromInternalValue(MakeClampedNum(n) * - Time::kMicrosecondsPerSecond); -} -template <typename T> -constexpr TimeDelta Milliseconds(T n) { - return TimeDelta::FromInternalValue(MakeClampedNum(n) * - Time::kMicrosecondsPerMillisecond); -} -template <typename T> -constexpr TimeDelta Microseconds(T n) { - return TimeDelta::FromInternalValue(MakeClampedNum(n)); -} -template <typename T> -constexpr TimeDelta Nanoseconds(T n) { - return TimeDelta::FromInternalValue(MakeClampedNum(n) / - Time::kNanosecondsPerMicrosecond); -} -template <typename T> -constexpr TimeDelta Hertz(T n) { - return n ? TimeDelta::FromInternalValue(Time::kMicrosecondsPerSecond / - MakeClampedNum(n)) - : TimeDelta::Max(); -} - -// TimeDelta functions that must appear below the declarations of Time/TimeDelta - -constexpr double TimeDelta::ToHz() const { - return Seconds(1) / *this; -} - -constexpr int TimeDelta::InHours() const { - // saturated_cast<> is necessary since very large (but still less than - // min/max) deltas would result in overflow. - return saturated_cast<int>(delta_ / Time::kMicrosecondsPerHour); -} - -constexpr int TimeDelta::InMinutes() const { - // saturated_cast<> is necessary since very large (but still less than - // min/max) deltas would result in overflow. - return saturated_cast<int>(delta_ / Time::kMicrosecondsPerMinute); -} - -constexpr double TimeDelta::InSecondsF() const { - if (!is_inf()) - return static_cast<double>(delta_) / Time::kMicrosecondsPerSecond; - return (delta_ < 0) ? -std::numeric_limits<double>::infinity() - : std::numeric_limits<double>::infinity(); -} - -constexpr int64_t TimeDelta::InSeconds() const { - return is_inf() ? delta_ : (delta_ / Time::kMicrosecondsPerSecond); -} - -constexpr int64_t TimeDelta::InNanoseconds() const { - return base::ClampMul(delta_, Time::kNanosecondsPerMicrosecond); -} - -// static -constexpr TimeDelta TimeDelta::Max() { - return TimeDelta(std::numeric_limits<int64_t>::max()); -} - -// static -constexpr TimeDelta TimeDelta::Min() { - return TimeDelta(std::numeric_limits<int64_t>::min()); -} - -// static -constexpr TimeDelta TimeDelta::FiniteMax() { - return TimeDelta(std::numeric_limits<int64_t>::max() - 1); -} - -// static -constexpr TimeDelta TimeDelta::FiniteMin() { - return TimeDelta(std::numeric_limits<int64_t>::min() + 1); -} - -// TimeBase functions that must appear below the declarations of Time/TimeDelta -namespace time_internal { - -template <class TimeClass> -constexpr TimeDelta TimeBase<TimeClass>::since_origin() const { - return Microseconds(us_); -} - -template <class TimeClass> -constexpr TimeDelta TimeBase<TimeClass>::operator-( - const TimeBase<TimeClass>& other) const { - return Microseconds(us_ - other.us_); -} - -template <class TimeClass> -constexpr TimeClass TimeBase<TimeClass>::operator+(TimeDelta delta) const { - return TimeClass((Microseconds(us_) + delta).InMicroseconds()); -} - -template <class TimeClass> -constexpr TimeClass TimeBase<TimeClass>::operator-(TimeDelta delta) const { - return TimeClass((Microseconds(us_) - delta).InMicroseconds()); -} - -} // namespace time_internal - -// Time functions that must appear below the declarations of Time/TimeDelta - -// static -constexpr Time Time::FromTimeT(time_t tt) { - if (tt == 0) - return Time(); // Preserve 0 so we can tell it doesn't exist. - return (tt == std::numeric_limits<time_t>::max()) - ? Max() - : (UnixEpoch() + Seconds(tt)); -} - -// TimeTicks ------------------------------------------------------------------ - -// Represents monotonically non-decreasing clock time. -class PA_COMPONENT_EXPORT(PARTITION_ALLOC) TimeTicks - : public time_internal::TimeBase<TimeTicks> { - public: - // The underlying clock used to generate new TimeTicks. - enum class Clock { - FUCHSIA_ZX_CLOCK_MONOTONIC, - LINUX_CLOCK_MONOTONIC, - IOS_CF_ABSOLUTE_TIME_MINUS_KERN_BOOTTIME, - MAC_MACH_ABSOLUTE_TIME, - WIN_QPC, - WIN_ROLLOVER_PROTECTED_TIME_GET_TIME - }; - - constexpr TimeTicks() : TimeBase(0) {} - - // Platform-dependent tick count representing "right now." When - // IsHighResolution() returns false, the resolution of the clock could be - // as coarse as ~15.6ms. Otherwise, the resolution should be no worse than one - // microsecond. - static TimeTicks Now(); - - // Returns true if the high resolution clock is working on this system and - // Now() will return high resolution values. Note that, on systems where the - // high resolution clock works but is deemed inefficient, the low resolution - // clock will be used instead. - [[nodiscard]] static bool IsHighResolution(); - - // Returns true if TimeTicks is consistent across processes, meaning that - // timestamps taken on different processes can be safely compared with one - // another. (Note that, even on platforms where this returns true, time values - // from different threads that are within one tick of each other must be - // considered to have an ambiguous ordering.) - [[nodiscard]] static bool IsConsistentAcrossProcesses(); - -#if BUILDFLAG(IS_FUCHSIA) - // Converts between TimeTicks and an ZX_CLOCK_MONOTONIC zx_time_t value. - static TimeTicks FromZxTime(zx_time_t nanos_since_boot); - zx_time_t ToZxTime() const; -#endif - -#if BUILDFLAG(IS_WIN) - // Translates an absolute QPC timestamp into a TimeTicks value. The returned - // value has the same origin as Now(). Do NOT attempt to use this if - // IsHighResolution() returns false. - static TimeTicks FromQPCValue(LONGLONG qpc_value); -#endif - -#if BUILDFLAG(IS_APPLE) -#if BUILDFLAG(PARTITION_ALLOC_ENABLE_MACH_ABSOLUTE_TIME_TICKS) - static TimeTicks FromMachAbsoluteTime(uint64_t mach_absolute_time); - - // Sets the current Mach timebase to `timebase`. Returns the old timebase. - static mach_timebase_info_data_t SetMachTimebaseInfoForTesting( - mach_timebase_info_data_t timebase); - -#endif // BUILDFLAG(PARTITION_ALLOC_ENABLE_MACH_ABSOLUTE_TIME_TICKS) -#endif // BUILDFLAG(IS_APPLE) - -#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(PA_IS_CHROMEOS_ASH) - // Converts to TimeTicks the value obtained from SystemClock.uptimeMillis(). - // Note: this conversion may be non-monotonic in relation to previously - // obtained TimeTicks::Now() values because of the truncation (to - // milliseconds) performed by uptimeMillis(). - static TimeTicks FromUptimeMillis(int64_t uptime_millis_value); - -#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(PA_IS_CHROMEOS_ASH) - -#if BUILDFLAG(IS_ANDROID) - // Converts to TimeTicks the value obtained from System.nanoTime(). This - // conversion will be monotonic in relation to previously obtained - // TimeTicks::Now() values as the clocks are based on the same posix monotonic - // clock, with nanoTime() potentially providing higher resolution. - static TimeTicks FromJavaNanoTime(int64_t nano_time_value); - - // Truncates the TimeTicks value to the precision of SystemClock#uptimeMillis. - // Note that the clocks already share the same monotonic clock source. - jlong ToUptimeMillis() const; - - // Returns the TimeTicks value as microseconds in the timebase of - // SystemClock#uptimeMillis. - // Note that the clocks already share the same monotonic clock source. - // - // System.nanoTime() may be used to get sub-millisecond precision in Java code - // and may be compared against this value as the two share the same clock - // source (though be sure to convert nanos to micros). - jlong ToUptimeMicros() const; - -#endif // BUILDFLAG(IS_ANDROID) - - // Get an estimate of the TimeTick value at the time of the UnixEpoch. Because - // Time and TimeTicks respond differently to user-set time and NTP - // adjustments, this number is only an estimate. Nevertheless, this can be - // useful when you need to relate the value of TimeTicks to a real time and - // date. Note: Upon first invocation, this function takes a snapshot of the - // realtime clock to establish a reference point. This function will return - // the same value for the duration of the application, but will be different - // in future application runs. - static TimeTicks UnixEpoch(); - - // Returns |this| snapped to the next tick, given a |tick_phase| and - // repeating |tick_interval| in both directions. |this| may be before, - // after, or equal to the |tick_phase|. - TimeTicks SnappedToNextTick(TimeTicks tick_phase, - TimeDelta tick_interval) const; - - // Returns an enum indicating the underlying clock being used to generate - // TimeTicks timestamps. This function should only be used for debugging and - // logging purposes. - static Clock GetClock(); - - // Converts an integer value representing TimeTicks to a class. This may be - // used when deserializing a |TimeTicks| structure, using a value known to be - // compatible. It is not provided as a constructor because the integer type - // may be unclear from the perspective of a caller. - // - // DEPRECATED - Do not use in new code. For deserializing TimeTicks values, - // prefer TimeTicks + TimeDelta(); however, be aware that the origin is not - // fixed and may vary. Serializing for persistence is strongly discouraged. - // http://crbug.com/634507 - static constexpr TimeTicks FromInternalValue(int64_t us) { - return TimeTicks(us); - } - - protected: -#if BUILDFLAG(IS_WIN) - typedef DWORD (*TickFunctionType)(void); - static TickFunctionType SetMockTickFunction(TickFunctionType ticker); -#endif - - private: - friend class time_internal::TimeBase<TimeTicks>; - - // Please use Now() to create a new object. This is for internal use - // and testing. - constexpr explicit TimeTicks(int64_t us) : TimeBase(us) {} -}; - -// ThreadTicks ---------------------------------------------------------------- - -// Represents a clock, specific to a particular thread, than runs only while the -// thread is running. -class PA_COMPONENT_EXPORT(PARTITION_ALLOC) ThreadTicks - : public time_internal::TimeBase<ThreadTicks> { - public: - constexpr ThreadTicks() : TimeBase(0) {} - - // Returns true if ThreadTicks::Now() is supported on this system. - [[nodiscard]] static bool IsSupported() { -#if (defined(_POSIX_THREAD_CPUTIME) && (_POSIX_THREAD_CPUTIME >= 0)) || \ - BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA) - return true; -#elif BUILDFLAG(IS_WIN) - return IsSupportedWin(); -#else - return false; -#endif - } - - // Waits until the initialization is completed. Needs to be guarded with a - // call to IsSupported(). - static void WaitUntilInitialized() { -#if BUILDFLAG(IS_WIN) - WaitUntilInitializedWin(); -#endif - } - - // Returns thread-specific CPU-time on systems that support this feature. - // Needs to be guarded with a call to IsSupported(). Use this timer - // to (approximately) measure how much time the calling thread spent doing - // actual work vs. being de-scheduled. May return bogus results if the thread - // migrates to another CPU between two calls. Returns an empty ThreadTicks - // object until the initialization is completed. If a clock reading is - // absolutely needed, call WaitUntilInitialized() before this method. - static ThreadTicks Now(); - -#if BUILDFLAG(IS_WIN) - // Similar to Now() above except this returns thread-specific CPU time for an - // arbitrary thread. All comments for Now() method above apply apply to this - // method as well. - static ThreadTicks GetForThread(const PlatformThreadHandle& thread_handle); -#endif - - // Converts an integer value representing ThreadTicks to a class. This may be - // used when deserializing a |ThreadTicks| structure, using a value known to - // be compatible. It is not provided as a constructor because the integer type - // may be unclear from the perspective of a caller. - // - // DEPRECATED - Do not use in new code. For deserializing ThreadTicks values, - // prefer ThreadTicks + TimeDelta(); however, be aware that the origin is not - // fixed and may vary. Serializing for persistence is strongly - // discouraged. http://crbug.com/634507 - static constexpr ThreadTicks FromInternalValue(int64_t us) { - return ThreadTicks(us); - } - - private: - friend class time_internal::TimeBase<ThreadTicks>; - - // Please use Now() or GetForThread() to create a new object. This is for - // internal use and testing. - constexpr explicit ThreadTicks(int64_t us) : TimeBase(us) {} - -#if BUILDFLAG(IS_WIN) - [[nodiscard]] static bool IsSupportedWin(); - static void WaitUntilInitializedWin(); -#endif -}; - -} // namespace partition_alloc::internal::base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_TIME_TIME_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/time/time_android.cc b/base/allocator/partition_allocator/partition_alloc_base/time/time_android.cc deleted file mode 100644 index fa814d2..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/time/time_android.cc +++ /dev/null
@@ -1,65 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/time/time.h" - -namespace partition_alloc::internal::base { - -// static -TimeTicks TimeTicks::FromUptimeMillis(int64_t uptime_millis_value) { - // The implementation of the SystemClock.uptimeMillis() in AOSP uses the same - // clock as base::TimeTicks::Now(): clock_gettime(CLOCK_MONOTONIC), see in - // platform/system/code: - // 1. libutils/SystemClock.cpp - // 2. libutils/Timers.cpp - // - // We are not aware of any motivations for Android OEMs to modify the AOSP - // implementation of either uptimeMillis() or clock_gettime(CLOCK_MONOTONIC), - // so we assume that there are no such customizations. - // - // Under these assumptions the conversion is as safe as copying the value of - // base::TimeTicks::Now() with a loss of sub-millisecond precision. - return TimeTicks(uptime_millis_value * Time::kMicrosecondsPerMillisecond); -} - -// This file is included on chromeos_ash because it needs to interpret -// UptimeMillis values from the Android container. -#if BUILDFLAG(IS_ANDROID) - -// static -TimeTicks TimeTicks::FromJavaNanoTime(int64_t nano_time_value) { - // The implementation of the System.nanoTime() in AOSP uses the same - // clock as UptimeMillis() and base::TimeTicks::Now(): - // clock_gettime(CLOCK_MONOTONIC), see ojluni/src/main/native/System.c in - // AOSP. - // - // From Android documentation on android.os.SystemClock: - // [uptimeMillis()] is the basis for most interval timing such as - // Thread.sleep(millls), Object.wait(millis), and System.nanoTime(). - // - // We are not aware of any motivations for Android OEMs to modify the AOSP - // implementation of either uptimeMillis(), nanoTime, or - // clock_gettime(CLOCK_MONOTONIC), so we assume that there are no such - // customizations. - // - // Under these assumptions the conversion is as safe as copying the value of - // base::TimeTicks::Now() without the (theoretical) sub-microsecond - // resolution. - return TimeTicks(nano_time_value / Time::kNanosecondsPerMicrosecond); -} - -jlong TimeTicks::ToUptimeMillis() const { - // See FromUptimeMillis. UptimeMillis and TimeTicks use the same clock source, - // and only differ in resolution. - return us_ / Time::kMicrosecondsPerMillisecond; -} - -jlong TimeTicks::ToUptimeMicros() const { - // Same as ToUptimeMillis but maintains sub-millisecond precision. - return us_; -} - -#endif // BUILDFLAG(IS_ANDROID) - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/time/time_conversion_posix.cc b/base/allocator/partition_allocator/partition_alloc_base/time/time_conversion_posix.cc deleted file mode 100644 index 7235c7b..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/time/time_conversion_posix.cc +++ /dev/null
@@ -1,67 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/time/time.h" - -#include <stdint.h> -#include <sys/time.h> -#include <time.h> - -#include <limits> - -#include "base/allocator/partition_allocator/partition_alloc_check.h" - -namespace partition_alloc::internal::base { - -// static -TimeDelta TimeDelta::FromTimeSpec(const timespec& ts) { - return TimeDelta(ts.tv_sec * Time::kMicrosecondsPerSecond + - ts.tv_nsec / Time::kNanosecondsPerMicrosecond); -} - -struct timespec TimeDelta::ToTimeSpec() const { - int64_t microseconds = InMicroseconds(); - time_t seconds = 0; - if (microseconds >= Time::kMicrosecondsPerSecond) { - seconds = InSeconds(); - microseconds -= seconds * Time::kMicrosecondsPerSecond; - } - struct timespec result = { - seconds, - static_cast<long>(microseconds * Time::kNanosecondsPerMicrosecond)}; - return result; -} - -// static -Time Time::FromTimeVal(struct timeval t) { - PA_DCHECK(t.tv_usec < static_cast<int>(Time::kMicrosecondsPerSecond)); - PA_DCHECK(t.tv_usec >= 0); - if (t.tv_usec == 0 && t.tv_sec == 0) - return Time(); - if (t.tv_usec == static_cast<suseconds_t>(Time::kMicrosecondsPerSecond) - 1 && - t.tv_sec == std::numeric_limits<time_t>::max()) - return Max(); - return Time((static_cast<int64_t>(t.tv_sec) * Time::kMicrosecondsPerSecond) + - t.tv_usec + kTimeTToMicrosecondsOffset); -} - -struct timeval Time::ToTimeVal() const { - struct timeval result; - if (is_null()) { - result.tv_sec = 0; - result.tv_usec = 0; - return result; - } - if (is_max()) { - result.tv_sec = std::numeric_limits<time_t>::max(); - result.tv_usec = static_cast<suseconds_t>(Time::kMicrosecondsPerSecond) - 1; - return result; - } - int64_t us = us_ - kTimeTToMicrosecondsOffset; - result.tv_sec = us / Time::kMicrosecondsPerSecond; - result.tv_usec = us % Time::kMicrosecondsPerSecond; - return result; -} - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/time/time_fuchsia.cc b/base/allocator/partition_allocator/partition_alloc_base/time/time_fuchsia.cc deleted file mode 100644 index 2f5fd48..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/time/time_fuchsia.cc +++ /dev/null
@@ -1,97 +0,0 @@ -// Copyright 2017 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/time/time.h" - -#include <threads.h> -#include <zircon/syscalls.h> -#include <zircon/threads.h> - -#include "base/allocator/partition_allocator/partition_alloc_base/time/time_override.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" - -namespace partition_alloc::internal::base { - -// Time ----------------------------------------------------------------------- - -namespace subtle { -Time TimeNowIgnoringOverride() { - timespec ts; - int status = timespec_get(&ts, TIME_UTC); - PA_CHECK(status != 0); - return Time::FromTimeSpec(ts); -} - -Time TimeNowFromSystemTimeIgnoringOverride() { - // Just use TimeNowIgnoringOverride() because it returns the system time. - return TimeNowIgnoringOverride(); -} -} // namespace subtle - -// TimeTicks ------------------------------------------------------------------ - -namespace subtle { -TimeTicks TimeTicksNowIgnoringOverride() { - const zx_time_t nanos_since_boot = zx_clock_get_monotonic(); - PA_CHECK(0 != nanos_since_boot); - return TimeTicks::FromZxTime(nanos_since_boot); -} -} // namespace subtle - -// static -TimeDelta TimeDelta::FromZxDuration(zx_duration_t nanos) { - return Nanoseconds(nanos); -} - -zx_duration_t TimeDelta::ToZxDuration() const { - return InNanoseconds(); -} - -// static -Time Time::FromZxTime(zx_time_t nanos_since_unix_epoch) { - return UnixEpoch() + Nanoseconds(nanos_since_unix_epoch); -} - -zx_time_t Time::ToZxTime() const { - return (*this - UnixEpoch()).InNanoseconds(); -} - -// static -TimeTicks::Clock TimeTicks::GetClock() { - return Clock::FUCHSIA_ZX_CLOCK_MONOTONIC; -} - -// static -bool TimeTicks::IsHighResolution() { - return true; -} - -// static -bool TimeTicks::IsConsistentAcrossProcesses() { - return true; -} - -// static -TimeTicks TimeTicks::FromZxTime(zx_time_t nanos_since_boot) { - return TimeTicks() + Nanoseconds(nanos_since_boot); -} - -zx_time_t TimeTicks::ToZxTime() const { - return (*this - TimeTicks()).InNanoseconds(); -} - -// ThreadTicks ---------------------------------------------------------------- - -namespace subtle { -ThreadTicks ThreadTicksNowIgnoringOverride() { - zx_info_thread_stats_t info; - zx_status_t status = zx_object_get_info(thrd_get_zx_handle(thrd_current()), - ZX_INFO_THREAD_STATS, &info, - sizeof(info), nullptr, nullptr); - PA_CHECK(status == ZX_OK); - return ThreadTicks() + Nanoseconds(info.total_runtime); -} -} // namespace subtle - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/time/time_mac.mm b/base/allocator/partition_allocator/partition_alloc_base/time/time_mac.mm deleted file mode 100644 index 5f98fc6..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/time/time_mac.mm +++ /dev/null
@@ -1,249 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/time/time.h" - -#import <Foundation/Foundation.h> -#include <mach/mach.h> -#include <mach/mach_time.h> -#include <stddef.h> -#include <stdint.h> -#include <sys/sysctl.h> -#include <sys/time.h> -#include <sys/types.h> -#include <time.h> - -#if BUILDFLAG(IS_IOS) -#include <errno.h> -#endif - -#include "base/allocator/partition_allocator/partition_alloc_base/logging.h" -#include "base/allocator/partition_allocator/partition_alloc_base/numerics/safe_conversions.h" -#include "base/allocator/partition_allocator/partition_alloc_base/time/time_override.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "build/build_config.h" - -namespace partition_alloc::internal::base { - -namespace { - -#if BUILDFLAG(PARTITION_ALLOC_ENABLE_MACH_ABSOLUTE_TIME_TICKS) -// Returns a pointer to the initialized Mach timebase info struct. -mach_timebase_info_data_t* MachTimebaseInfo() { - static mach_timebase_info_data_t timebase_info = []() { - mach_timebase_info_data_t info; - kern_return_t kr = mach_timebase_info(&info); - PA_DCHECK(kr == KERN_SUCCESS) << "mach_timebase_info"; - PA_DCHECK(info.numer); - PA_DCHECK(info.denom); - return info; - }(); - return &timebase_info; -} - -int64_t MachTimeToMicroseconds(uint64_t mach_time) { - // timebase_info gives us the conversion factor between absolute time tick - // units and nanoseconds. - mach_timebase_info_data_t* timebase_info = MachTimebaseInfo(); - - // Take the fast path when the conversion is 1:1. The result will for sure fit - // into an int_64 because we're going from nanoseconds to microseconds. - if (timebase_info->numer == timebase_info->denom) { - return static_cast<int64_t>(mach_time / Time::kNanosecondsPerMicrosecond); - } - - uint64_t microseconds = 0; - const uint64_t divisor = - timebase_info->denom * Time::kNanosecondsPerMicrosecond; - - // Microseconds is mach_time * timebase.numer / - // (timebase.denom * kNanosecondsPerMicrosecond). Divide first to reduce - // the chance of overflow. Also stash the remainder right now, a likely - // byproduct of the division. - microseconds = mach_time / divisor; - const uint64_t mach_time_remainder = mach_time % divisor; - - // Now multiply, keeping an eye out for overflow. - PA_CHECK(!__builtin_umulll_overflow(microseconds, timebase_info->numer, - µseconds)); - - // By dividing first we lose precision. Regain it by adding back the - // microseconds from the remainder, with an eye out for overflow. - uint64_t least_significant_microseconds = - (mach_time_remainder * timebase_info->numer) / divisor; - PA_CHECK(!__builtin_uaddll_overflow( - microseconds, least_significant_microseconds, µseconds)); - - // Don't bother with the rollover handling that the Windows version does. - // The returned time in microseconds is enough for 292,277 years (starting - // from 2^63 because the returned int64_t is signed, - // 9223372036854775807 / (1e6 * 60 * 60 * 24 * 365.2425) = 292,277). - return checked_cast<int64_t>(microseconds); -} -#endif // BUILDFLAG(PARTITION_ALLOC_ENABLE_MACH_ABSOLUTE_TIME_TICKS) - -// Returns monotonically growing number of ticks in microseconds since some -// unspecified starting point. -int64_t ComputeCurrentTicks() { -#if !BUILDFLAG(PARTITION_ALLOC_ENABLE_MACH_ABSOLUTE_TIME_TICKS) - struct timespec tp; - // clock_gettime() returns 0 on success and -1 on failure. Failure can only - // happen because of bad arguments (unsupported clock type or timespec - // pointer out of accessible address space). Here it is known that neither - // can happen since the timespec parameter is stack allocated right above and - // `CLOCK_MONOTONIC` is supported on all versions of iOS that Chrome is - // supported on. - int res = clock_gettime(CLOCK_MONOTONIC, &tp); - PA_DCHECK(0 == res) << "Failed clock_gettime, errno: " << errno; - - return (int64_t)tp.tv_sec * 1000000 + tp.tv_nsec / 1000; -#else - // mach_absolute_time is it when it comes to ticks on the Mac. Other calls - // with less precision (such as TickCount) just call through to - // mach_absolute_time. - return MachTimeToMicroseconds(mach_absolute_time()); -#endif // !BUILDFLAG(PARTITION_ALLOC_ENABLE_MACH_ABSOLUTE_TIME_TICKS) -} - -int64_t ComputeThreadTicks() { - // The pthreads library keeps a cached reference to the thread port, which - // does not have to be released like mach_thread_self() does. - mach_port_t thread_port = pthread_mach_thread_np(pthread_self()); - if (thread_port == MACH_PORT_NULL) { - PA_DLOG(ERROR) << "Failed to get pthread_mach_thread_np()"; - return 0; - } - - mach_msg_type_number_t thread_info_count = THREAD_BASIC_INFO_COUNT; - thread_basic_info_data_t thread_info_data; - - kern_return_t kr = thread_info( - thread_port, THREAD_BASIC_INFO, - reinterpret_cast<thread_info_t>(&thread_info_data), &thread_info_count); - PA_DCHECK(kr == KERN_SUCCESS) << "thread_info"; - - CheckedNumeric<int64_t> absolute_micros(thread_info_data.user_time.seconds + - thread_info_data.system_time.seconds); - absolute_micros *= Time::kMicrosecondsPerSecond; - absolute_micros += (thread_info_data.user_time.microseconds + - thread_info_data.system_time.microseconds); - return absolute_micros.ValueOrDie(); -} - -} // namespace - -// The Time routines in this file use Mach and CoreFoundation APIs, since the -// POSIX definition of time_t in Mac OS X wraps around after 2038--and -// there are already cookie expiration dates, etc., past that time out in -// the field. Using CFDate prevents that problem, and using mach_absolute_time -// for TimeTicks gives us nice high-resolution interval timing. - -// Time ----------------------------------------------------------------------- - -namespace subtle { -Time TimeNowIgnoringOverride() { - return Time::FromCFAbsoluteTime(CFAbsoluteTimeGetCurrent()); -} - -Time TimeNowFromSystemTimeIgnoringOverride() { - // Just use TimeNowIgnoringOverride() because it returns the system time. - return TimeNowIgnoringOverride(); -} -} // namespace subtle - -// static -Time Time::FromCFAbsoluteTime(CFAbsoluteTime t) { - static_assert(std::numeric_limits<CFAbsoluteTime>::has_infinity, - "CFAbsoluteTime must have an infinity value"); - if (t == 0) - return Time(); // Consider 0 as a null Time. - return (t == std::numeric_limits<CFAbsoluteTime>::infinity()) - ? Max() - : (UnixEpoch() + - Seconds(double{t + kCFAbsoluteTimeIntervalSince1970})); -} - -CFAbsoluteTime Time::ToCFAbsoluteTime() const { - static_assert(std::numeric_limits<CFAbsoluteTime>::has_infinity, - "CFAbsoluteTime must have an infinity value"); - if (is_null()) - return 0; // Consider 0 as a null Time. - return is_max() ? std::numeric_limits<CFAbsoluteTime>::infinity() - : (CFAbsoluteTime{(*this - UnixEpoch()).InSecondsF()} - - kCFAbsoluteTimeIntervalSince1970); -} - -// static -Time Time::FromNSDate(NSDate* date) { - PA_DCHECK(date); - return FromCFAbsoluteTime(date.timeIntervalSinceReferenceDate); -} - -NSDate* Time::ToNSDate() const { - return [NSDate dateWithTimeIntervalSinceReferenceDate:ToCFAbsoluteTime()]; -} - -// TimeDelta ------------------------------------------------------------------ - -#if BUILDFLAG(PARTITION_ALLOC_ENABLE_MACH_ABSOLUTE_TIME_TICKS) -// static -TimeDelta TimeDelta::FromMachTime(uint64_t mach_time) { - return Microseconds(MachTimeToMicroseconds(mach_time)); -} -#endif // BUILDFLAG(PARTITION_ALLOC_ENABLE_MACH_ABSOLUTE_TIME_TICKS) - -// TimeTicks ------------------------------------------------------------------ - -namespace subtle { -TimeTicks TimeTicksNowIgnoringOverride() { - return TimeTicks() + Microseconds(ComputeCurrentTicks()); -} -} // namespace subtle - -// static -bool TimeTicks::IsHighResolution() { - return true; -} - -// static -bool TimeTicks::IsConsistentAcrossProcesses() { - return true; -} - -#if BUILDFLAG(PARTITION_ALLOC_ENABLE_MACH_ABSOLUTE_TIME_TICKS) -// static -TimeTicks TimeTicks::FromMachAbsoluteTime(uint64_t mach_absolute_time) { - return TimeTicks(MachTimeToMicroseconds(mach_absolute_time)); -} - -// static -mach_timebase_info_data_t TimeTicks::SetMachTimebaseInfoForTesting( - mach_timebase_info_data_t timebase) { - mach_timebase_info_data_t orig_timebase = *MachTimebaseInfo(); - - *MachTimebaseInfo() = timebase; - - return orig_timebase; -} - -#endif // BUILDFLAG(PARTITION_ALLOC_ENABLE_MACH_ABSOLUTE_TIME_TICKS) - -// static -TimeTicks::Clock TimeTicks::GetClock() { -#if !BUILDFLAG(PARTITION_ALLOC_ENABLE_MACH_ABSOLUTE_TIME_TICKS) - return Clock::IOS_CF_ABSOLUTE_TIME_MINUS_KERN_BOOTTIME; -#else - return Clock::MAC_MACH_ABSOLUTE_TIME; -#endif // !BUILDFLAG(PARTITION_ALLOC_ENABLE_MACH_ABSOLUTE_TIME_TICKS) -} - -// ThreadTicks ---------------------------------------------------------------- - -namespace subtle { -ThreadTicks ThreadTicksNowIgnoringOverride() { - return ThreadTicks() + Microseconds(ComputeThreadTicks()); -} -} // namespace subtle - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/time/time_now_posix.cc b/base/allocator/partition_allocator/partition_alloc_base/time/time_now_posix.cc deleted file mode 100644 index fa55e46..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/time/time_now_posix.cc +++ /dev/null
@@ -1,123 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/time/time.h" - -#include <stdint.h> -#include <sys/time.h> -#include <time.h> - -#include "build/build_config.h" -#if BUILDFLAG(IS_ANDROID) && !defined(__LP64__) -#include <time64.h> -#endif -#include <unistd.h> - -#include "base/allocator/partition_allocator/partition_alloc_base/numerics/safe_math.h" -#include "base/allocator/partition_allocator/partition_alloc_base/time/time_override.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/partition_alloc_notreached.h" - -// Ensure the Fuchsia and Mac builds do not include this module. Instead, -// non-POSIX implementation is used for sampling the system clocks. -#if BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_APPLE) -#error "This implementation is for POSIX platforms other than Fuchsia or Mac." -#endif - -namespace partition_alloc::internal::base { - -namespace { - -int64_t ConvertTimespecToMicros(const struct timespec& ts) { - // On 32-bit systems, the calculation cannot overflow int64_t. - // 2**32 * 1000000 + 2**64 / 1000 < 2**63 - if (sizeof(ts.tv_sec) <= 4 && sizeof(ts.tv_nsec) <= 8) { - int64_t result = ts.tv_sec; - result *= Time::kMicrosecondsPerSecond; - result += (ts.tv_nsec / Time::kNanosecondsPerMicrosecond); - return result; - } - CheckedNumeric<int64_t> result(ts.tv_sec); - result *= Time::kMicrosecondsPerSecond; - result += (ts.tv_nsec / Time::kNanosecondsPerMicrosecond); - return result.ValueOrDie(); -} - -// Helper function to get results from clock_gettime() and convert to a -// microsecond timebase. Minimum requirement is MONOTONIC_CLOCK to be supported -// on the system. FreeBSD 6 has CLOCK_MONOTONIC but defines -// _POSIX_MONOTONIC_CLOCK to -1. -#if (BUILDFLAG(IS_POSIX) && defined(_POSIX_MONOTONIC_CLOCK) && \ - _POSIX_MONOTONIC_CLOCK >= 0) || \ - BUILDFLAG(IS_BSD) || BUILDFLAG(IS_ANDROID) -int64_t ClockNow(clockid_t clk_id) { - struct timespec ts; - PA_CHECK(clock_gettime(clk_id, &ts) == 0); - return ConvertTimespecToMicros(ts); -} -#else // _POSIX_MONOTONIC_CLOCK -#error No usable tick clock function on this platform. -#endif // _POSIX_MONOTONIC_CLOCK - -} // namespace - -// Time ----------------------------------------------------------------------- - -namespace subtle { -Time TimeNowIgnoringOverride() { - struct timeval tv; - struct timezone tz = {0, 0}; // UTC - PA_CHECK(gettimeofday(&tv, &tz) == 0); - // Combine seconds and microseconds in a 64-bit field containing microseconds - // since the epoch. That's enough for nearly 600 centuries. Adjust from - // Unix (1970) to Windows (1601) epoch. - return Time() + - Microseconds((tv.tv_sec * Time::kMicrosecondsPerSecond + tv.tv_usec) + - Time::kTimeTToMicrosecondsOffset); -} - -Time TimeNowFromSystemTimeIgnoringOverride() { - // Just use TimeNowIgnoringOverride() because it returns the system time. - return TimeNowIgnoringOverride(); -} -} // namespace subtle - -// TimeTicks ------------------------------------------------------------------ - -namespace subtle { -TimeTicks TimeTicksNowIgnoringOverride() { - return TimeTicks() + Microseconds(ClockNow(CLOCK_MONOTONIC)); -} -} // namespace subtle - -// static -TimeTicks::Clock TimeTicks::GetClock() { - return Clock::LINUX_CLOCK_MONOTONIC; -} - -// static -bool TimeTicks::IsHighResolution() { - return true; -} - -// static -bool TimeTicks::IsConsistentAcrossProcesses() { - return true; -} - -// ThreadTicks ---------------------------------------------------------------- - -namespace subtle { -ThreadTicks ThreadTicksNowIgnoringOverride() { -#if (defined(_POSIX_THREAD_CPUTIME) && (_POSIX_THREAD_CPUTIME >= 0)) || \ - BUILDFLAG(IS_ANDROID) - return ThreadTicks() + Microseconds(ClockNow(CLOCK_THREAD_CPUTIME_ID)); -#else - PA_NOTREACHED(); - return ThreadTicks(); -#endif -} -} // namespace subtle - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/time/time_override.cc b/base/allocator/partition_allocator/partition_alloc_base/time/time_override.cc deleted file mode 100644 index 691e374..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/time/time_override.cc +++ /dev/null
@@ -1,45 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_base/time/time_override.h" - -#include "base/allocator/partition_allocator/partition_alloc_check.h" - -namespace partition_alloc::internal::base::subtle { - -// static -bool ScopedTimeClockOverrides::overrides_active_ = false; - -ScopedTimeClockOverrides::ScopedTimeClockOverrides( - TimeNowFunction time_override, - TimeTicksNowFunction time_ticks_override, - ThreadTicksNowFunction thread_ticks_override) { - PA_DCHECK(!overrides_active_); - overrides_active_ = true; - if (time_override) { - internal::g_time_now_function.store(time_override, - std::memory_order_relaxed); - internal::g_time_now_from_system_time_function.store( - time_override, std::memory_order_relaxed); - } - if (time_ticks_override) { - internal::g_time_ticks_now_function.store(time_ticks_override, - std::memory_order_relaxed); - } - if (thread_ticks_override) { - internal::g_thread_ticks_now_function.store(thread_ticks_override, - std::memory_order_relaxed); - } -} - -ScopedTimeClockOverrides::~ScopedTimeClockOverrides() { - internal::g_time_now_function.store(&TimeNowIgnoringOverride); - internal::g_time_now_from_system_time_function.store( - &TimeNowFromSystemTimeIgnoringOverride); - internal::g_time_ticks_now_function.store(&TimeTicksNowIgnoringOverride); - internal::g_thread_ticks_now_function.store(&ThreadTicksNowIgnoringOverride); - overrides_active_ = false; -} - -} // namespace partition_alloc::internal::base::subtle
diff --git a/base/allocator/partition_allocator/partition_alloc_base/time/time_override.h b/base/allocator/partition_allocator/partition_alloc_base/time/time_override.h deleted file mode 100644 index 69f4772..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/time/time_override.h +++ /dev/null
@@ -1,85 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_TIME_TIME_OVERRIDE_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_TIME_TIME_OVERRIDE_H_ - -#include <atomic> - -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_base/time/time.h" -#include "build/build_config.h" - -namespace partition_alloc::internal::base { - -using TimeNowFunction = decltype(&Time::Now); -using TimeTicksNowFunction = decltype(&TimeTicks::Now); -using ThreadTicksNowFunction = decltype(&ThreadTicks::Now); - -// Time overrides should be used with extreme caution. Discuss with //base/time -// OWNERS before adding a new one. -namespace subtle { - -// Override the return value of Time::Now and Time::NowFromSystemTime / -// TimeTicks::Now / ThreadTicks::Now to emulate time, e.g. for tests or to -// modify progression of time. It is recommended that the override be set while -// single-threaded and before the first call to Now() to avoid threading issues -// and inconsistencies in returned values. Overriding time while other threads -// are running is very subtle and should be reserved for developer only use -// cases (e.g. virtual time in devtools) where any flakiness caused by a racy -// time update isn't surprising. Instantiating a ScopedTimeClockOverrides while -// other threads are running might break their expectation that TimeTicks and -// ThreadTicks increase monotonically. Nested overrides are not allowed. -class PA_COMPONENT_EXPORT(PARTITION_ALLOC) ScopedTimeClockOverrides { - public: - // Pass |nullptr| for any override if it shouldn't be overriden. - ScopedTimeClockOverrides(TimeNowFunction time_override, - TimeTicksNowFunction time_ticks_override, - ThreadTicksNowFunction thread_ticks_override); - - ScopedTimeClockOverrides(const ScopedTimeClockOverrides&) = delete; - ScopedTimeClockOverrides& operator=(const ScopedTimeClockOverrides&) = delete; - - // Restores the platform default Now() functions. - ~ScopedTimeClockOverrides(); - - static bool overrides_active() { return overrides_active_; } - - private: - static bool overrides_active_; -}; - -// These methods return the platform default Time::Now / TimeTicks::Now / -// ThreadTicks::Now values even while an override is in place. These methods -// should only be used in places where emulated time should be disregarded. For -// example, they can be used to implement test timeouts for tests that may -// override time. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) Time TimeNowIgnoringOverride(); -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -Time TimeNowFromSystemTimeIgnoringOverride(); -PA_COMPONENT_EXPORT(PARTITION_ALLOC) TimeTicks TimeTicksNowIgnoringOverride(); -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -ThreadTicks ThreadTicksNowIgnoringOverride(); - -} // namespace subtle - -namespace internal { - -// These function pointers are used by platform-independent implementations of -// the Now() methods and ScopedTimeClockOverrides. They are set to point to the -// respective NowIgnoringOverride functions by default, but can also be set by -// platform-specific code to select a default implementation at runtime, thereby -// avoiding the indirection via the NowIgnoringOverride functions. Note that the -// pointers can be overridden and later reset to the NowIgnoringOverride -// functions by ScopedTimeClockOverrides. -extern std::atomic<TimeNowFunction> g_time_now_function; -extern std::atomic<TimeNowFunction> g_time_now_from_system_time_function; -extern std::atomic<TimeTicksNowFunction> g_time_ticks_now_function; -extern std::atomic<ThreadTicksNowFunction> g_thread_ticks_now_function; - -} // namespace internal - -} // namespace partition_alloc::internal::base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_TIME_TIME_OVERRIDE_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/time/time_win.cc b/base/allocator/partition_allocator/partition_alloc_base/time/time_win.cc deleted file mode 100644 index 43fa17d..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/time/time_win.cc +++ /dev/null
@@ -1,726 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Windows Timer Primer -// -// A good article: http://www.ddj.com/windows/184416651 -// A good mozilla bug: http://bugzilla.mozilla.org/show_bug.cgi?id=363258 -// -// The default windows timer, GetSystemTimeAsFileTime is not very precise. -// It is only good to ~15.5ms. -// -// QueryPerformanceCounter is the logical choice for a high-precision timer. -// However, it is known to be buggy on some hardware. Specifically, it can -// sometimes "jump". On laptops, QPC can also be very expensive to call. -// It's 3-4x slower than timeGetTime() on desktops, but can be 10x slower -// on laptops. A unittest exists which will show the relative cost of various -// timers on any system. -// -// The next logical choice is timeGetTime(). timeGetTime has a precision of -// 1ms, but only if you call APIs (timeBeginPeriod()) which affect all other -// applications on the system. By default, precision is only 15.5ms. -// Unfortunately, we don't want to call timeBeginPeriod because we don't -// want to affect other applications. Further, on mobile platforms, use of -// faster multimedia timers can hurt battery life. See the intel -// article about this here: -// http://softwarecommunity.intel.com/articles/eng/1086.htm -// -// To work around all this, we're going to generally use timeGetTime(). We -// will only increase the system-wide timer if we're not running on battery -// power. - -#include "base/allocator/partition_allocator/partition_alloc_base/time/time.h" - -#include <windows.foundation.h> -#include <windows.h> - -#include <mmsystem.h> - -#include <stdint.h> - -#include <atomic> - -#include "base/allocator/partition_allocator/partition_alloc_base/bit_cast.h" -#include "base/allocator/partition_allocator/partition_alloc_base/cpu.h" -#include "base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread.h" -#include "base/allocator/partition_allocator/partition_alloc_base/time/time_override.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/partition_lock.h" -#include "build/build_config.h" - -namespace partition_alloc::internal::base { - -namespace { - -// From MSDN, FILETIME "Contains a 64-bit value representing the number of -// 100-nanosecond intervals since January 1, 1601 (UTC)." -int64_t FileTimeToMicroseconds(const FILETIME& ft) { - // Need to bit_cast to fix alignment, then divide by 10 to convert - // 100-nanoseconds to microseconds. This only works on little-endian - // machines. - return bit_cast<int64_t, FILETIME>(ft) / 10; -} - -bool CanConvertToFileTime(int64_t us) { - return us >= 0 && us <= (std::numeric_limits<int64_t>::max() / 10); -} - -FILETIME MicrosecondsToFileTime(int64_t us) { - PA_DCHECK(CanConvertToFileTime(us)) << "Out-of-range: Cannot convert " << us - << " microseconds to FILETIME units."; - - // Multiply by 10 to convert microseconds to 100-nanoseconds. Bit_cast will - // handle alignment problems. This only works on little-endian machines. - return bit_cast<FILETIME, int64_t>(us * 10); -} - -int64_t CurrentWallclockMicroseconds() { - FILETIME ft; - ::GetSystemTimeAsFileTime(&ft); - return FileTimeToMicroseconds(ft); -} - -// Time between resampling the un-granular clock for this API. -constexpr TimeDelta kMaxTimeToAvoidDrift = Seconds(60); - -int64_t g_initial_time = 0; -TimeTicks g_initial_ticks; - -void InitializeClock() { - g_initial_ticks = subtle::TimeTicksNowIgnoringOverride(); - g_initial_time = CurrentWallclockMicroseconds(); -} - -// Track the last value passed to timeBeginPeriod so that we can cancel that -// call by calling timeEndPeriod with the same value. A value of zero means that -// the timer frequency is not currently raised. -UINT g_last_interval_requested_ms = 0; -// Track if kMinTimerIntervalHighResMs or kMinTimerIntervalLowResMs is active. -// For most purposes this could also be named g_is_on_ac_power. -bool g_high_res_timer_enabled = false; -// How many times the high resolution timer has been called. -uint32_t g_high_res_timer_count = 0; -// Start time of the high resolution timer usage monitoring. This is needed -// to calculate the usage as percentage of the total elapsed time. -TimeTicks g_high_res_timer_usage_start; -// The cumulative time the high resolution timer has been in use since -// |g_high_res_timer_usage_start| moment. -TimeDelta g_high_res_timer_usage; -// Timestamp of the last activation change of the high resolution timer. This -// is used to calculate the cumulative usage. -TimeTicks g_high_res_timer_last_activation; -// The lock to control access to the above set of variables. -::partition_alloc::internal::Lock& GetHighResLock() { - static ::partition_alloc::internal::Lock lock; - return lock; -} - -// The two values that ActivateHighResolutionTimer uses to set the systemwide -// timer interrupt frequency on Windows. These control how precise timers are -// but also have a big impact on battery life. - -// Used when a faster timer has been requested (g_high_res_timer_count > 0) and -// the computer is running on AC power (plugged in) so that it's okay to go to -// the highest frequency. -constexpr UINT kMinTimerIntervalHighResMs = 1; - -// Used when a faster timer has been requested (g_high_res_timer_count > 0) and -// the computer is running on DC power (battery) so that we don't want to raise -// the timer frequency as much. -constexpr UINT kMinTimerIntervalLowResMs = 8; - -// Calculate the desired timer interrupt interval. Note that zero means that the -// system default should be used. -UINT GetIntervalMs() { - if (!g_high_res_timer_count) - return 0; // Use the default, typically 15.625 - if (g_high_res_timer_enabled) - return kMinTimerIntervalHighResMs; - return kMinTimerIntervalLowResMs; -} - -// Compare the currently requested timer interrupt interval to the last interval -// requested and update if necessary (by cancelling the old request and making a -// new request). If there is no change then do nothing. -void UpdateTimerIntervalLocked() { - UINT new_interval = GetIntervalMs(); - if (new_interval == g_last_interval_requested_ms) - return; - if (g_last_interval_requested_ms) { - // Record how long the timer interrupt frequency was raised. - g_high_res_timer_usage += subtle::TimeTicksNowIgnoringOverride() - - g_high_res_timer_last_activation; - // Reset the timer interrupt back to the default. - timeEndPeriod(g_last_interval_requested_ms); - } - g_last_interval_requested_ms = new_interval; - if (g_last_interval_requested_ms) { - // Record when the timer interrupt was raised. - g_high_res_timer_last_activation = subtle::TimeTicksNowIgnoringOverride(); - timeBeginPeriod(g_last_interval_requested_ms); - } -} - -// Returns the current value of the performance counter. -uint64_t QPCNowRaw() { - LARGE_INTEGER perf_counter_now = {}; - // According to the MSDN documentation for QueryPerformanceCounter(), this - // will never fail on systems that run XP or later. - // https://msdn.microsoft.com/library/windows/desktop/ms644904.aspx - ::QueryPerformanceCounter(&perf_counter_now); - return perf_counter_now.QuadPart; -} - -} // namespace - -// Time ----------------------------------------------------------------------- - -namespace subtle { -Time TimeNowIgnoringOverride() { - if (g_initial_time == 0) - InitializeClock(); - - // We implement time using the high-resolution timers so that we can get - // timeouts which are smaller than 10-15ms. If we just used - // CurrentWallclockMicroseconds(), we'd have the less-granular timer. - // - // To make this work, we initialize the clock (g_initial_time) and the - // counter (initial_ctr). To compute the initial time, we can check - // the number of ticks that have elapsed, and compute the delta. - // - // To avoid any drift, we periodically resync the counters to the system - // clock. - while (true) { - TimeTicks ticks = TimeTicksNowIgnoringOverride(); - - // Calculate the time elapsed since we started our timer - TimeDelta elapsed = ticks - g_initial_ticks; - - // Check if enough time has elapsed that we need to resync the clock. - if (elapsed > kMaxTimeToAvoidDrift) { - InitializeClock(); - continue; - } - - return Time() + elapsed + Microseconds(g_initial_time); - } -} - -Time TimeNowFromSystemTimeIgnoringOverride() { - // Force resync. - InitializeClock(); - return Time() + Microseconds(g_initial_time); -} -} // namespace subtle - -// static -Time Time::FromFileTime(FILETIME ft) { - if (bit_cast<int64_t, FILETIME>(ft) == 0) - return Time(); - if (ft.dwHighDateTime == std::numeric_limits<DWORD>::max() && - ft.dwLowDateTime == std::numeric_limits<DWORD>::max()) - return Max(); - return Time(FileTimeToMicroseconds(ft)); -} - -FILETIME Time::ToFileTime() const { - if (is_null()) - return bit_cast<FILETIME, int64_t>(0); - if (is_max()) { - FILETIME result; - result.dwHighDateTime = std::numeric_limits<DWORD>::max(); - result.dwLowDateTime = std::numeric_limits<DWORD>::max(); - return result; - } - return MicrosecondsToFileTime(us_); -} - -// static -// Enable raising of the system-global timer interrupt frequency to 1 kHz (when -// enable is true, which happens when on AC power) or some lower frequency when -// on battery power (when enable is false). If the g_high_res_timer_enabled -// setting hasn't actually changed or if if there are no outstanding requests -// (if g_high_res_timer_count is zero) then do nothing. -// TL;DR - call this when going from AC to DC power or vice-versa. -void Time::EnableHighResolutionTimer(bool enable) { - ScopedGuard lock(GetHighResLock()); - g_high_res_timer_enabled = enable; - UpdateTimerIntervalLocked(); -} - -// static -// Request that the system-global Windows timer interrupt frequency be raised. -// How high the frequency is raised depends on the system's power state and -// possibly other options. -// TL;DR - call this at the beginning and end of a time period where you want -// higher frequency timer interrupts. Each call with activating=true must be -// paired with a subsequent activating=false call. -bool Time::ActivateHighResolutionTimer(bool activating) { - // We only do work on the transition from zero to one or one to zero so we - // can easily undo the effect (if necessary) when EnableHighResolutionTimer is - // called. - const uint32_t max = std::numeric_limits<uint32_t>::max(); - - ScopedGuard lock(GetHighResLock()); - if (activating) { - PA_DCHECK(g_high_res_timer_count != max); - ++g_high_res_timer_count; - } else { - PA_DCHECK(g_high_res_timer_count != 0u); - --g_high_res_timer_count; - } - UpdateTimerIntervalLocked(); - return true; -} - -// static -// See if the timer interrupt interval has been set to the lowest value. -bool Time::IsHighResolutionTimerInUse() { - ScopedGuard lock(GetHighResLock()); - return g_last_interval_requested_ms == kMinTimerIntervalHighResMs; -} - -// static -void Time::ResetHighResolutionTimerUsage() { - ScopedGuard lock(GetHighResLock()); - g_high_res_timer_usage = TimeDelta(); - g_high_res_timer_usage_start = subtle::TimeTicksNowIgnoringOverride(); - if (g_high_res_timer_count > 0) - g_high_res_timer_last_activation = g_high_res_timer_usage_start; -} - -// static -double Time::GetHighResolutionTimerUsage() { - ScopedGuard lock(GetHighResLock()); - TimeTicks now = subtle::TimeTicksNowIgnoringOverride(); - TimeDelta elapsed_time = now - g_high_res_timer_usage_start; - if (elapsed_time.is_zero()) { - // This is unexpected but possible if TimeTicks resolution is low and - // GetHighResolutionTimerUsage() is called promptly after - // ResetHighResolutionTimerUsage(). - return 0.0; - } - TimeDelta used_time = g_high_res_timer_usage; - if (g_high_res_timer_count > 0) { - // If currently activated add the remainder of time since the last - // activation. - used_time += now - g_high_res_timer_last_activation; - } - return used_time / elapsed_time * 100; -} - -// TimeTicks ------------------------------------------------------------------ - -namespace { - -// We define a wrapper to adapt between the __stdcall and __cdecl call of the -// mock function, and to avoid a static constructor. Assigning an import to a -// function pointer directly would require setup code to fetch from the IAT. -DWORD timeGetTimeWrapper() { - return timeGetTime(); -} - -DWORD (*g_tick_function)(void) = &timeGetTimeWrapper; - -// A structure holding the most significant bits of "last seen" and a -// "rollover" counter. -union LastTimeAndRolloversState { - // The state as a single 32-bit opaque value. - std::atomic<int32_t> as_opaque_32{0}; - - // The state as usable values. - struct { - // The top 8-bits of the "last" time. This is enough to check for rollovers - // and the small bit-size means fewer CompareAndSwap operations to store - // changes in state, which in turn makes for fewer retries. - uint8_t last_8; - // A count of the number of detected rollovers. Using this as bits 47-32 - // of the upper half of a 64-bit value results in a 48-bit tick counter. - // This extends the total rollover period from about 49 days to about 8800 - // years while still allowing it to be stored with last_8 in a single - // 32-bit value. - uint16_t rollovers; - } as_values; -}; -std::atomic<int32_t> g_last_time_and_rollovers = 0; -static_assert(sizeof(LastTimeAndRolloversState) <= - sizeof(g_last_time_and_rollovers), - "LastTimeAndRolloversState does not fit in a single atomic word"); - -// We use timeGetTime() to implement TimeTicks::Now(). This can be problematic -// because it returns the number of milliseconds since Windows has started, -// which will roll over the 32-bit value every ~49 days. We try to track -// rollover ourselves, which works if TimeTicks::Now() is called at least every -// 48.8 days (not 49 days because only changes in the top 8 bits get noticed). -TimeTicks RolloverProtectedNow() { - LastTimeAndRolloversState state; - DWORD now; // DWORD is always unsigned 32 bits. - - while (true) { - // Fetch the "now" and "last" tick values, updating "last" with "now" and - // incrementing the "rollovers" counter if the tick-value has wrapped back - // around. Atomic operations ensure that both "last" and "rollovers" are - // always updated together. - int32_t original = - g_last_time_and_rollovers.load(std::memory_order_acquire); - state.as_opaque_32 = original; - now = g_tick_function(); - uint8_t now_8 = static_cast<uint8_t>(now >> 24); - if (now_8 < state.as_values.last_8) - ++state.as_values.rollovers; - state.as_values.last_8 = now_8; - - // If the state hasn't changed, exit the loop. - if (state.as_opaque_32 == original) - break; - - // Save the changed state. If the existing value is unchanged from the - // original, exit the loop. - int32_t check = g_last_time_and_rollovers.compare_exchange_strong( - original, state.as_opaque_32, std::memory_order_release); - if (check == original) - break; - - // Another thread has done something in between so retry from the top. - } - - return TimeTicks() + - Milliseconds(now + - (static_cast<uint64_t>(state.as_values.rollovers) << 32)); -} - -// Discussion of tick counter options on Windows: -// -// (1) CPU cycle counter. (Retrieved via RDTSC) -// The CPU counter provides the highest resolution time stamp and is the least -// expensive to retrieve. However, on older CPUs, two issues can affect its -// reliability: First it is maintained per processor and not synchronized -// between processors. Also, the counters will change frequency due to thermal -// and power changes, and stop in some states. -// -// (2) QueryPerformanceCounter (QPC). The QPC counter provides a high- -// resolution (<1 microsecond) time stamp. On most hardware running today, it -// auto-detects and uses the constant-rate RDTSC counter to provide extremely -// efficient and reliable time stamps. -// -// On older CPUs where RDTSC is unreliable, it falls back to using more -// expensive (20X to 40X more costly) alternate clocks, such as HPET or the ACPI -// PM timer, and can involve system calls; and all this is up to the HAL (with -// some help from ACPI). According to -// http://blogs.msdn.com/oldnewthing/archive/2005/09/02/459952.aspx, in the -// worst case, it gets the counter from the rollover interrupt on the -// programmable interrupt timer. In best cases, the HAL may conclude that the -// RDTSC counter runs at a constant frequency, then it uses that instead. On -// multiprocessor machines, it will try to verify the values returned from -// RDTSC on each processor are consistent with each other, and apply a handful -// of workarounds for known buggy hardware. In other words, QPC is supposed to -// give consistent results on a multiprocessor computer, but for older CPUs it -// can be unreliable due bugs in BIOS or HAL. -// -// (3) System time. The system time provides a low-resolution (from ~1 to ~15.6 -// milliseconds) time stamp but is comparatively less expensive to retrieve and -// more reliable. Time::EnableHighResolutionTimer() and -// Time::ActivateHighResolutionTimer() can be called to alter the resolution of -// this timer; and also other Windows applications can alter it, affecting this -// one. - -TimeTicks InitialNowFunction(); - -// See "threading notes" in InitializeNowFunctionPointer() for details on how -// concurrent reads/writes to these globals has been made safe. -std::atomic<TimeTicksNowFunction> g_time_ticks_now_ignoring_override_function{ - &InitialNowFunction}; -int64_t g_qpc_ticks_per_second = 0; - -TimeDelta QPCValueToTimeDelta(LONGLONG qpc_value) { - // Ensure that the assignment to |g_qpc_ticks_per_second|, made in - // InitializeNowFunctionPointer(), has happened by this point. - std::atomic_thread_fence(std::memory_order_acquire); - - PA_DCHECK(g_qpc_ticks_per_second > 0); - - // If the QPC Value is below the overflow threshold, we proceed with - // simple multiply and divide. - if (qpc_value < Time::kQPCOverflowThreshold) { - return Microseconds(qpc_value * Time::kMicrosecondsPerSecond / - g_qpc_ticks_per_second); - } - // Otherwise, calculate microseconds in a round about manner to avoid - // overflow and precision issues. - int64_t whole_seconds = qpc_value / g_qpc_ticks_per_second; - int64_t leftover_ticks = qpc_value - (whole_seconds * g_qpc_ticks_per_second); - return Microseconds((whole_seconds * Time::kMicrosecondsPerSecond) + - ((leftover_ticks * Time::kMicrosecondsPerSecond) / - g_qpc_ticks_per_second)); -} - -TimeTicks QPCNow() { - return TimeTicks() + QPCValueToTimeDelta(QPCNowRaw()); -} - -void InitializeNowFunctionPointer() { - LARGE_INTEGER ticks_per_sec = {}; - if (!QueryPerformanceFrequency(&ticks_per_sec)) - ticks_per_sec.QuadPart = 0; - - // If Windows cannot provide a QPC implementation, TimeTicks::Now() must use - // the low-resolution clock. - // - // If the QPC implementation is expensive and/or unreliable, TimeTicks::Now() - // will still use the low-resolution clock. A CPU lacking a non-stop time - // counter will cause Windows to provide an alternate QPC implementation that - // works, but is expensive to use. - // - // Otherwise, Now uses the high-resolution QPC clock. As of 21 August 2015, - // ~72% of users fall within this category. - CPU cpu; - const TimeTicksNowFunction now_function = - (ticks_per_sec.QuadPart <= 0 || !cpu.has_non_stop_time_stamp_counter()) - ? &RolloverProtectedNow - : &QPCNow; - - // Threading note 1: In an unlikely race condition, it's possible for two or - // more threads to enter InitializeNowFunctionPointer() in parallel. This is - // not a problem since all threads end up writing out the same values - // to the global variables, and those variable being atomic are safe to read - // from other threads. - // - // Threading note 2: A release fence is placed here to ensure, from the - // perspective of other threads using the function pointers, that the - // assignment to |g_qpc_ticks_per_second| happens before the function pointers - // are changed. - g_qpc_ticks_per_second = ticks_per_sec.QuadPart; - std::atomic_thread_fence(std::memory_order_release); - // Also set g_time_ticks_now_function to avoid the additional indirection via - // TimeTicksNowIgnoringOverride() for future calls to TimeTicks::Now(), only - // if it wasn't already overridden to a different value. memory_order_relaxed - // is sufficient since an explicit fence was inserted above. - base::TimeTicksNowFunction initial_time_ticks_now_function = - &subtle::TimeTicksNowIgnoringOverride; - internal::g_time_ticks_now_function.compare_exchange_strong( - initial_time_ticks_now_function, now_function, std::memory_order_relaxed); - g_time_ticks_now_ignoring_override_function.store(now_function, - std::memory_order_relaxed); -} - -TimeTicks InitialNowFunction() { - InitializeNowFunctionPointer(); - return g_time_ticks_now_ignoring_override_function.load( - std::memory_order_relaxed)(); -} - -} // namespace - -// static -TimeTicks::TickFunctionType TimeTicks::SetMockTickFunction( - TickFunctionType ticker) { - TickFunctionType old = g_tick_function; - g_tick_function = ticker; - g_last_time_and_rollovers.store(0, std::memory_order_relaxed); - return old; -} - -namespace subtle { -TimeTicks TimeTicksNowIgnoringOverride() { - return g_time_ticks_now_ignoring_override_function.load( - std::memory_order_relaxed)(); -} -} // namespace subtle - -// static -bool TimeTicks::IsHighResolution() { - if (g_time_ticks_now_ignoring_override_function == &InitialNowFunction) - InitializeNowFunctionPointer(); - return g_time_ticks_now_ignoring_override_function == &QPCNow; -} - -// static -bool TimeTicks::IsConsistentAcrossProcesses() { - // According to Windows documentation [1] QPC is consistent post-Windows - // Vista. So if we are using QPC then we are consistent which is the same as - // being high resolution. - // - // [1] - // https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx - // - // "In general, the performance counter results are consistent across all - // processors in multi-core and multi-processor systems, even when measured on - // different threads or processes. Here are some exceptions to this rule: - // - Pre-Windows Vista operating systems that run on certain processors might - // violate this consistency because of one of these reasons: - // 1. The hardware processors have a non-invariant TSC and the BIOS - // doesn't indicate this condition correctly. - // 2. The TSC synchronization algorithm that was used wasn't suitable for - // systems with large numbers of processors." - return IsHighResolution(); -} - -// static -TimeTicks::Clock TimeTicks::GetClock() { - return IsHighResolution() ? Clock::WIN_QPC - : Clock::WIN_ROLLOVER_PROTECTED_TIME_GET_TIME; -} - -// ThreadTicks ---------------------------------------------------------------- - -namespace subtle { -ThreadTicks ThreadTicksNowIgnoringOverride() { - return ThreadTicks::GetForThread(PlatformThread::CurrentHandle()); -} -} // namespace subtle - -// static -ThreadTicks ThreadTicks::GetForThread( - const PlatformThreadHandle& thread_handle) { - PA_DCHECK(IsSupported()); - -#if defined(ARCH_CPU_ARM64) - // QueryThreadCycleTime versus TSCTicksPerSecond doesn't have much relation to - // actual elapsed time on Windows on Arm, because QueryThreadCycleTime is - // backed by the actual number of CPU cycles executed, rather than a - // constant-rate timer like Intel. To work around this, use GetThreadTimes - // (which isn't as accurate but is meaningful as a measure of elapsed - // per-thread time). - FILETIME creation_time, exit_time, kernel_time, user_time; - ::GetThreadTimes(thread_handle.platform_handle(), &creation_time, &exit_time, - &kernel_time, &user_time); - - const int64_t us = FileTimeToMicroseconds(user_time); -#else - // Get the number of TSC ticks used by the current thread. - ULONG64 thread_cycle_time = 0; - ::QueryThreadCycleTime(thread_handle.platform_handle(), &thread_cycle_time); - - // Get the frequency of the TSC. - const double tsc_ticks_per_second = time_internal::TSCTicksPerSecond(); - if (tsc_ticks_per_second == 0) - return ThreadTicks(); - - // Return the CPU time of the current thread. - const double thread_time_seconds = thread_cycle_time / tsc_ticks_per_second; - const int64_t us = - static_cast<int64_t>(thread_time_seconds * Time::kMicrosecondsPerSecond); -#endif - - return ThreadTicks(us); -} - -// static -bool ThreadTicks::IsSupportedWin() { -#if defined(ARCH_CPU_ARM64) - // The Arm implementation does not use QueryThreadCycleTime and therefore does - // not care about the time stamp counter. - return true; -#else - return time_internal::HasConstantRateTSC(); -#endif -} - -// static -void ThreadTicks::WaitUntilInitializedWin() { -#if !defined(ARCH_CPU_ARM64) - while (time_internal::TSCTicksPerSecond() == 0) - ::Sleep(10); -#endif -} - -// static -TimeTicks TimeTicks::FromQPCValue(LONGLONG qpc_value) { - return TimeTicks() + QPCValueToTimeDelta(qpc_value); -} - -// TimeDelta ------------------------------------------------------------------ - -// static -TimeDelta TimeDelta::FromQPCValue(LONGLONG qpc_value) { - return QPCValueToTimeDelta(qpc_value); -} - -// static -TimeDelta TimeDelta::FromFileTime(FILETIME ft) { - return Microseconds(FileTimeToMicroseconds(ft)); -} - -// static -TimeDelta TimeDelta::FromWinrtDateTime(ABI::Windows::Foundation::DateTime dt) { - // UniversalTime is 100 ns intervals since January 1, 1601 (UTC) - return Microseconds(dt.UniversalTime / 10); -} - -ABI::Windows::Foundation::DateTime TimeDelta::ToWinrtDateTime() const { - ABI::Windows::Foundation::DateTime date_time; - date_time.UniversalTime = InMicroseconds() * 10; - return date_time; -} - -#if !defined(ARCH_CPU_ARM64) -namespace time_internal { - -bool HasConstantRateTSC() { - static bool is_supported = CPU().has_non_stop_time_stamp_counter(); - return is_supported; -} - -double TSCTicksPerSecond() { - PA_DCHECK(HasConstantRateTSC()); - // The value returned by QueryPerformanceFrequency() cannot be used as the TSC - // frequency, because there is no guarantee that the TSC frequency is equal to - // the performance counter frequency. - // The TSC frequency is cached in a static variable because it takes some time - // to compute it. - static double tsc_ticks_per_second = 0; - if (tsc_ticks_per_second != 0) - return tsc_ticks_per_second; - - // Increase the thread priority to reduces the chances of having a context - // switch during a reading of the TSC and the performance counter. - const int previous_priority = ::GetThreadPriority(::GetCurrentThread()); - ::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_HIGHEST); - - // The first time that this function is called, make an initial reading of the - // TSC and the performance counter. - - static const uint64_t tsc_initial = __rdtsc(); - static const uint64_t perf_counter_initial = QPCNowRaw(); - - // Make a another reading of the TSC and the performance counter every time - // that this function is called. - const uint64_t tsc_now = __rdtsc(); - const uint64_t perf_counter_now = QPCNowRaw(); - - // Reset the thread priority. - ::SetThreadPriority(::GetCurrentThread(), previous_priority); - - // Make sure that at least 50 ms elapsed between the 2 readings. The first - // time that this function is called, we don't expect this to be the case. - // Note: The longer the elapsed time between the 2 readings is, the more - // accurate the computed TSC frequency will be. The 50 ms value was - // chosen because local benchmarks show that it allows us to get a - // stddev of less than 1 tick/us between multiple runs. - // Note: According to the MSDN documentation for QueryPerformanceFrequency(), - // this will never fail on systems that run XP or later. - // https://msdn.microsoft.com/library/windows/desktop/ms644905.aspx - LARGE_INTEGER perf_counter_frequency = {}; - ::QueryPerformanceFrequency(&perf_counter_frequency); - PA_DCHECK(perf_counter_now >= perf_counter_initial); - const uint64_t perf_counter_ticks = perf_counter_now - perf_counter_initial; - const double elapsed_time_seconds = - perf_counter_ticks / static_cast<double>(perf_counter_frequency.QuadPart); - - constexpr double kMinimumEvaluationPeriodSeconds = 0.05; - if (elapsed_time_seconds < kMinimumEvaluationPeriodSeconds) - return 0; - - // Compute the frequency of the TSC. - PA_DCHECK(tsc_now >= tsc_initial); - const uint64_t tsc_ticks = tsc_now - tsc_initial; - tsc_ticks_per_second = tsc_ticks / elapsed_time_seconds; - - return tsc_ticks_per_second; -} - -} // namespace time_internal -#endif // defined(ARCH_CPU_ARM64) - -} // namespace partition_alloc::internal::base
diff --git a/base/allocator/partition_allocator/partition_alloc_base/types/strong_alias.h b/base/allocator/partition_allocator/partition_alloc_base/types/strong_alias.h deleted file mode 100644 index 1c139d8..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/types/strong_alias.h +++ /dev/null
@@ -1,141 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_TYPES_STRONG_ALIAS_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_TYPES_STRONG_ALIAS_H_ - -#include <type_traits> -#include <utility> - -namespace partition_alloc::internal::base { - -// A type-safe alternative for a typedef or a 'using' directive. -// -// C++ currently does not support type-safe typedefs, despite multiple proposals -// (ex. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3515.pdf). The -// next best thing is to try and emulate them in library code. -// -// The motivation is to disallow several classes of errors: -// -// using Orange = int; -// using Apple = int; -// Apple apple(2); -// Orange orange = apple; // Orange should not be able to become an Apple. -// Orange x = orange + apple; // Shouldn't add Oranges and Apples. -// if (orange > apple); // Shouldn't compare Apples to Oranges. -// void foo(Orange); -// void foo(Apple); // Redefinition. -// etc. -// -// StrongAlias may instead be used as follows: -// -// using Orange = StrongAlias<class OrangeTag, int>; -// using Apple = StrongAlias<class AppleTag, int>; -// using Banana = StrongAlias<class BananaTag, std::string>; -// Apple apple(2); -// Banana banana("Hello"); -// Orange orange = apple; // Does not compile. -// Orange other_orange = orange; // Compiles, types match. -// Orange x = orange + apple; // Does not compile. -// Orange y = Orange(orange.value() + apple.value()); // Compiles. -// Orange z = Orange(banana->size() + *other_orange); // Compiles. -// if (orange > apple); // Does not compile. -// if (orange > other_orange); // Compiles. -// void foo(Orange); -// void foo(Apple); // Compiles into separate overload. -// -// StrongAlias is a zero-cost abstraction, it's compiled away. -// -// TagType is an empty tag class (also called "phantom type") that only serves -// the type system to differentiate between different instantiations of the -// template. -// UnderlyingType may be almost any value type. Note that some methods of the -// StrongAlias may be unavailable (ie. produce elaborate compilation errors when -// used) if UnderlyingType doesn't support them. -// -// StrongAlias only directly exposes comparison operators (for convenient use in -// ordered containers) and a Hasher struct (for unordered_map/set). It's -// impossible, without reflection, to expose all methods of the UnderlyingType -// in StrongAlias's interface. It's also potentially unwanted (ex. you don't -// want to be able to add two StrongAliases that represent socket handles). -// A getter and dereference operators are provided in case you need to access -// the UnderlyingType. -// -// See also -// - //styleguide/c++/blink-c++.md which provides recommendation and examples of -// using StrongAlias<Tag, bool> instead of a bare bool. -// - IdType<...> which provides helpers for specializing StrongAlias to be -// used as an id. -// - TokenType<...> which provides helpers for specializing StrongAlias to be -// used as a wrapper of base::UnguessableToken. -template <typename TagType, typename UnderlyingType> -class StrongAlias { - public: - constexpr StrongAlias() = default; - constexpr explicit StrongAlias(const UnderlyingType& v) : value_(v) {} - constexpr explicit StrongAlias(UnderlyingType&& v) noexcept - : value_(std::move(v)) {} - - constexpr UnderlyingType* operator->() { return &value_; } - constexpr const UnderlyingType* operator->() const { return &value_; } - - constexpr UnderlyingType& operator*() & { return value_; } - constexpr const UnderlyingType& operator*() const& { return value_; } - constexpr UnderlyingType&& operator*() && { return std::move(value_); } - constexpr const UnderlyingType&& operator*() const&& { - return std::move(value_); - } - - constexpr UnderlyingType& value() & { return value_; } - constexpr const UnderlyingType& value() const& { return value_; } - constexpr UnderlyingType&& value() && { return std::move(value_); } - constexpr const UnderlyingType&& value() const&& { return std::move(value_); } - - constexpr explicit operator const UnderlyingType&() const& { return value_; } - - constexpr bool operator==(const StrongAlias& other) const { - return value_ == other.value_; - } - constexpr bool operator!=(const StrongAlias& other) const { - return value_ != other.value_; - } - constexpr bool operator<(const StrongAlias& other) const { - return value_ < other.value_; - } - constexpr bool operator<=(const StrongAlias& other) const { - return value_ <= other.value_; - } - constexpr bool operator>(const StrongAlias& other) const { - return value_ > other.value_; - } - constexpr bool operator>=(const StrongAlias& other) const { - return value_ >= other.value_; - } - - // Hasher to use in std::unordered_map, std::unordered_set, etc. - // - // Example usage: - // using MyType = base::StrongAlias<...>; - // using MySet = std::unordered_set<MyType, typename MyType::Hasher>; - // - // https://google.github.io/styleguide/cppguide.html#std_hash asks to avoid - // defining specializations of `std::hash` - this is why the hasher needs to - // be explicitly specified and why the following code will *not* work: - // using MyType = base::StrongAlias<...>; - // using MySet = std::unordered_set<MyType>; // This won't work. - struct Hasher { - using argument_type = StrongAlias; - using result_type = std::size_t; - result_type operator()(const argument_type& id) const { - return std::hash<UnderlyingType>()(id.value()); - } - }; - - protected: - UnderlyingType value_; -}; - -} // namespace partition_alloc::internal::base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_TYPES_STRONG_ALIAS_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/win/win_handle_types.h b/base/allocator/partition_allocator/partition_alloc_base/win/win_handle_types.h deleted file mode 100644 index 9e41b4c..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/win/win_handle_types.h +++ /dev/null
@@ -1,16 +0,0 @@ -// 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. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_WIN_WIN_HANDLE_TYPES_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_WIN_WIN_HANDLE_TYPES_H_ - -// Forward declare Windows compatible handles. - -#define PA_WINDOWS_HANDLE_TYPE(name) \ - struct name##__; \ - typedef struct name##__* name; -#include "base/allocator/partition_allocator/partition_alloc_base/win/win_handle_types_list.inc" -#undef PA_WINDOWS_HANDLE_TYPE - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_WIN_WIN_HANDLE_TYPES_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_base/win/win_handle_types_list.inc b/base/allocator/partition_allocator/partition_alloc_base/win/win_handle_types_list.inc deleted file mode 100644 index 8d730ff..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/win/win_handle_types_list.inc +++ /dev/null
@@ -1,25 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file intentionally does not have header guards, it's included -// inside a macro to generate enum values. The following line silences a -// presubmit and Tricium warning that would otherwise be triggered by this: -// no-include-guard-because-multiply-included -// NOLINT(build/header_guard) - -// This file contains the list of Windows handle types that can be recognized -// via specific pointee types declared in //base/win/windows_types.h -// (e.g. `HDC` points to a fake/forward-declared `HDC__` struct). - -PA_WINDOWS_HANDLE_TYPE(HDC) -PA_WINDOWS_HANDLE_TYPE(HDESK) -PA_WINDOWS_HANDLE_TYPE(HGLRC) -PA_WINDOWS_HANDLE_TYPE(HICON) -PA_WINDOWS_HANDLE_TYPE(HINSTANCE) -PA_WINDOWS_HANDLE_TYPE(HKEY) -PA_WINDOWS_HANDLE_TYPE(HKL) -PA_WINDOWS_HANDLE_TYPE(HMENU) -PA_WINDOWS_HANDLE_TYPE(HWINSTA) -PA_WINDOWS_HANDLE_TYPE(HWND) -PA_WINDOWS_HANDLE_TYPE(HMONITOR)
diff --git a/base/allocator/partition_allocator/partition_alloc_base/win/windows_types.h b/base/allocator/partition_allocator/partition_alloc_base/win/windows_types.h deleted file mode 100644 index 768b7ca..0000000 --- a/base/allocator/partition_allocator/partition_alloc_base/win/windows_types.h +++ /dev/null
@@ -1,88 +0,0 @@ -// Copyright 2014 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file contains defines and typedefs that allow popular Windows types to -// be used without the overhead of including windows.h. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_WIN_WINDOWS_TYPES_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_WIN_WINDOWS_TYPES_H_ - -// Needed for function prototypes. -#include <specstrings.h> - -#ifdef __cplusplus -extern "C" { -#endif - -// typedef and define the most commonly used Windows integer types. - -typedef unsigned long DWORD; -typedef long LONG; -typedef __int64 LONGLONG; -typedef unsigned __int64 ULONGLONG; - -#define VOID void -typedef char CHAR; -typedef short SHORT; -typedef long LONG; -typedef int INT; -typedef unsigned int UINT; -typedef unsigned int* PUINT; -typedef unsigned __int64 UINT64; -typedef void* LPVOID; -typedef void* PVOID; -typedef void* HANDLE; -typedef int BOOL; -typedef unsigned char BYTE; -typedef BYTE BOOLEAN; -typedef DWORD ULONG; -typedef unsigned short WORD; -typedef WORD UWORD; -typedef WORD ATOM; - -// Forward declare some Windows struct/typedef sets. - -typedef struct _RTL_SRWLOCK RTL_SRWLOCK; -typedef RTL_SRWLOCK SRWLOCK, *PSRWLOCK; - -typedef struct _FILETIME FILETIME; - -struct PA_CHROME_SRWLOCK { - PVOID Ptr; -}; - -// The trailing white-spaces after this macro are required, for compatibility -// with the definition in winnt.h. -#define RTL_SRWLOCK_INIT {0} // NOLINT -#define SRWLOCK_INIT RTL_SRWLOCK_INIT - -// clang-format on - -// Define some macros needed when prototyping Windows functions. - -#define DECLSPEC_IMPORT __declspec(dllimport) -#define WINBASEAPI DECLSPEC_IMPORT -#define WINAPI __stdcall - -// Needed for LockImpl. -WINBASEAPI _Releases_exclusive_lock_(*SRWLock) VOID WINAPI - ReleaseSRWLockExclusive(_Inout_ PSRWLOCK SRWLock); -WINBASEAPI BOOLEAN WINAPI TryAcquireSRWLockExclusive(_Inout_ PSRWLOCK SRWLock); - -// Needed for thread_local_storage.h -WINBASEAPI LPVOID WINAPI TlsGetValue(_In_ DWORD dwTlsIndex); - -WINBASEAPI BOOL WINAPI TlsSetValue(_In_ DWORD dwTlsIndex, - _In_opt_ LPVOID lpTlsValue); - -WINBASEAPI _Check_return_ _Post_equals_last_error_ DWORD WINAPI - GetLastError(VOID); - -WINBASEAPI VOID WINAPI SetLastError(_In_ DWORD dwErrCode); - -#ifdef __cplusplus -} -#endif - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_WIN_WINDOWS_TYPES_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_check.h b/base/allocator/partition_allocator/partition_alloc_check.h deleted file mode 100644 index 4539abc..0000000 --- a/base/allocator/partition_allocator/partition_alloc_check.h +++ /dev/null
@@ -1,172 +0,0 @@ -// 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 BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_CHECK_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_CHECK_H_ - -#include <cstdint> - -#include "base/allocator/partition_allocator/page_allocator_constants.h" -#include "base/allocator/partition_allocator/partition_alloc_base/check.h" -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/alias.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "build/build_config.h" - -#define PA_STRINGIFY_IMPL(s) #s -#define PA_STRINGIFY(s) PA_STRINGIFY_IMPL(s) - -// When PartitionAlloc is used as the default allocator, we cannot use the -// regular (D)CHECK() macros, as they allocate internally. When an assertion is -// triggered, they format strings, leading to reentrancy in the code, which none -// of PartitionAlloc is designed to support (and especially not for error -// paths). -// -// As a consequence: -// - When PartitionAlloc is not malloc(), use the regular macros -// - Otherwise, crash immediately. This provides worse error messages though. -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -// For official build discard log strings to reduce binary bloat. -#if !CHECK_WILL_STREAM() -// See base/check.h for implementation details. -#define PA_CHECK(condition) \ - PA_UNLIKELY(!(condition)) ? PA_IMMEDIATE_CRASH() \ - : PA_EAT_CHECK_STREAM_PARAMS() -#else -// PartitionAlloc uses async-signal-safe RawCheck() for error reporting. -// Async-signal-safe functions are guaranteed to not allocate as otherwise they -// could operate with inconsistent allocator state. -#define PA_CHECK(condition) \ - PA_UNLIKELY(!(condition)) \ - ? ::partition_alloc::internal::logging::RawCheck( \ - __FILE__ "(" PA_STRINGIFY(__LINE__) ") Check failed: " #condition) \ - : PA_EAT_CHECK_STREAM_PARAMS() -#endif // !CHECK_WILL_STREAM() - -#if BUILDFLAG(PA_DCHECK_IS_ON) -#define PA_DCHECK(condition) PA_CHECK(condition) -#else -#define PA_DCHECK(condition) PA_EAT_CHECK_STREAM_PARAMS(!(condition)) -#endif // BUILDFLAG(PA_DCHECK_IS_ON) - -#define PA_PCHECK(condition) \ - if (!(condition)) { \ - int error = errno; \ - ::partition_alloc::internal::base::debug::Alias(&error); \ - PA_IMMEDIATE_CRASH(); \ - } - -#if BUILDFLAG(PA_DCHECK_IS_ON) -#define PA_DPCHECK(condition) PA_PCHECK(condition) -#else -#define PA_DPCHECK(condition) PA_EAT_CHECK_STREAM_PARAMS(!(condition)) -#endif // BUILDFLAG(PA_DCHECK_IS_ON) - -#else -#define PA_CHECK(condition) PA_BASE_CHECK(condition) -#define PA_DCHECK(condition) PA_BASE_DCHECK(condition) -#define PA_PCHECK(condition) PA_BASE_PCHECK(condition) -#define PA_DPCHECK(condition) PA_BASE_DPCHECK(condition) -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - -// Expensive dchecks that run within *Scan. These checks are only enabled in -// debug builds with dchecks enabled. -#if !defined(NDEBUG) -#define PA_SCAN_DCHECK_IS_ON() BUILDFLAG(PA_DCHECK_IS_ON) -#else -#define PA_SCAN_DCHECK_IS_ON() 0 -#endif - -#if PA_SCAN_DCHECK_IS_ON() -#define PA_SCAN_DCHECK(expr) PA_DCHECK(expr) -#else -#define PA_SCAN_DCHECK(expr) PA_EAT_CHECK_STREAM_PARAMS(!(expr)) -#endif - -#if defined(PAGE_ALLOCATOR_CONSTANTS_ARE_CONSTEXPR) - -// Use this macro to assert on things that are conditionally constexpr as -// determined by PAGE_ALLOCATOR_CONSTANTS_ARE_CONSTEXPR or -// PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR. Where fixed at compile time, this -// is a static_assert. Where determined at run time, this is a PA_CHECK. -// Therefore, this macro must only be used where both a static_assert and a -// PA_CHECK would be viable, that is, within a function, and ideally a function -// that executes only once, early in the program, such as during initialization. -#define STATIC_ASSERT_OR_PA_CHECK(condition, message) \ - static_assert(condition, message) - -#else - -#define STATIC_ASSERT_OR_PA_CHECK(condition, message) \ - do { \ - PA_CHECK(condition) << (message); \ - } while (false) - -#endif - -// alignas(16) DebugKv causes breakpad_unittests and sandbox_linux_unittests -// failures on android-marshmallow-x86-rel because of SIGSEGV. -#if BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_X86_FAMILY) && \ - defined(ARCH_CPU_32_BITS) -#define PA_DEBUGKV_ALIGN alignas(8) -#else -#define PA_DEBUGKV_ALIGN alignas(16) -#endif - -namespace partition_alloc::internal { - -// Used for PA_DEBUG_DATA_ON_STACK, below. -struct PA_DEBUGKV_ALIGN DebugKv { - // 16 bytes object aligned on 16 bytes, to make it easier to see in crash - // reports. - char k[8] = {}; // Not necessarily 0-terminated. - uint64_t v = 0; - - DebugKv(const char* key, uint64_t value) : v(value) { - // Fill with ' ', so that the stack dump is nicer to read. Not using - // memset() on purpose, this header is included from *many* places. - for (int index = 0; index < 8; index++) { - k[index] = ' '; - } - - for (int index = 0; index < 8; index++) { - k[index] = key[index]; - if (key[index] == '\0') { - break; - } - } - } -}; - -} // namespace partition_alloc::internal - -#define PA_CONCAT(x, y) x##y -#define PA_CONCAT2(x, y) PA_CONCAT(x, y) -#define PA_DEBUG_UNIQUE_NAME PA_CONCAT2(kv, __LINE__) - -// Puts a key-value pair on the stack for debugging. `base::debug::Alias()` -// makes sure a local variable is saved on the stack, but the variables can be -// hard to find in crash reports, particularly if the frame pointer is not -// present / invalid. -// -// This puts a key right before the value on the stack. The key has to be a C -// string, which gets truncated if it's longer than 8 characters. -// Example use: -// PA_DEBUG_DATA_ON_STACK("size", 0x42) -// -// Sample output in lldb: -// (lldb) x 0x00007fffffffd0d0 0x00007fffffffd0f0 -// 0x7fffffffd0d0: 73 69 7a 65 00 00 00 00 42 00 00 00 00 00 00 00 -// size............ -// -// With gdb, one can use: -// x/8g <STACK_POINTER> -// to see the data. With lldb, "x <STACK_POINTER> <FRAME_POJNTER>" can be used. -#define PA_DEBUG_DATA_ON_STACK(name, value) \ - ::partition_alloc::internal::DebugKv PA_DEBUG_UNIQUE_NAME{name, value}; \ - ::partition_alloc::internal::base::debug::Alias(&PA_DEBUG_UNIQUE_NAME); - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_CHECK_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_config.h b/base/allocator/partition_allocator/partition_alloc_config.h deleted file mode 100644 index da59d55..0000000 --- a/base/allocator/partition_allocator/partition_alloc_config.h +++ /dev/null
@@ -1,335 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_CONFIG_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_CONFIG_H_ - -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "build/build_config.h" - -// PA_CONFIG() uses a similar trick as BUILDFLAG() to allow the compiler catch -// typos or a missing #include. -// -// ----------------------------------------------------------------------------- -// Housekeeping Rules -// ----------------------------------------------------------------------------- -// 1. Prefix all config macros in this file with PA_CONFIG_ and define them in -// a function-like manner, e.g. PA_CONFIG_MY_SETTING(). -// 2. Both positive and negative cases must be defined. -// 3. Don't use PA_CONFIG_MY_SETTING() directly outside of this file, use -// PA_CONFIG(flag-without-PA_CONFIG_) instead, e.g. PA_CONFIG(MY_SETTING). -// 4. Do not use PA_CONFIG() when defining config macros, or it will lead to -// recursion. Either use #if/#else, or PA_CONFIG_MY_SETTING() directly. -// 5. Try to use constexpr instead of macros wherever possible. -// TODO(bartekn): Convert macros to constexpr or BUILDFLAG as much as possible. -#define PA_CONFIG(flag) (PA_CONFIG_##flag()) - -// Assert that the heuristic in partition_alloc.gni is accurate on supported -// configurations. -#if BUILDFLAG(HAS_64_BIT_POINTERS) -static_assert(sizeof(void*) == 8, ""); -#else -static_assert(sizeof(void*) != 8, ""); -#endif // PA_CONFIG(HAS_64_BITS_POINTERS) - -#if BUILDFLAG(HAS_64_BIT_POINTERS) && \ - (defined(__ARM_NEON) || defined(__ARM_NEON__)) && defined(__ARM_FP) -#define PA_CONFIG_STARSCAN_NEON_SUPPORTED() 1 -#else -#define PA_CONFIG_STARSCAN_NEON_SUPPORTED() 0 -#endif - -#if BUILDFLAG(HAS_64_BIT_POINTERS) && BUILDFLAG(IS_IOS) -// Allow PA to select an alternate pool size at run-time before initialization, -// rather than using a single constexpr value. -// -// This is needed on iOS because iOS test processes can't handle large pools -// (see crbug.com/1250788). -// -// This setting is specific to 64-bit, as 32-bit has a different implementation. -#define PA_CONFIG_DYNAMICALLY_SELECT_POOL_SIZE() 1 -#else -#define PA_CONFIG_DYNAMICALLY_SELECT_POOL_SIZE() 0 -#endif // BUILDFLAG(HAS_64_BIT_POINTERS) && BUILDFLAG(IS_IOS) - -// 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. -// -// This setting is specific to 64-bit, as 32-bit has a different implementation. -#if BUILDFLAG(HAS_64_BIT_POINTERS) && BUILDFLAG(GLUE_CORE_POOLS) -#define PA_CONFIG_GLUE_CORE_POOLS() 1 -#else -#define PA_CONFIG_GLUE_CORE_POOLS() 0 -#endif - -#if BUILDFLAG(HAS_64_BIT_POINTERS) && \ - (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID)) -#include <linux/version.h> -// TODO(bikineev): Enable for ChromeOS. -#define PA_CONFIG_STARSCAN_UFFD_WRITE_PROTECTOR_SUPPORTED() \ - (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)) -#else -#define PA_CONFIG_STARSCAN_UFFD_WRITE_PROTECTOR_SUPPORTED() 0 -#endif // BUILDFLAG(HAS_64_BIT_POINTERS) && - // (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID)) - -#if BUILDFLAG(USE_STARSCAN) -// Use card table to avoid races for PCScan configuration without safepoints. -// The card table provides the guaranteee that for a marked card the underling -// super-page is fully initialized. -#define PA_CONFIG_STARSCAN_USE_CARD_TABLE() 1 -#else -// The card table is permanently disabled for 32-bit. -#define PA_CONFIG_STARSCAN_USE_CARD_TABLE() 0 -#endif // BUILDFLAG(USE_STARSCAN) - -// Use batched freeing when sweeping pages. This builds up a freelist in the -// scanner thread and appends to the slot-span's freelist only once. -#define PA_CONFIG_STARSCAN_BATCHED_FREE() 1 - -// TODO(bikineev): Temporarily disable inlining in *Scan to get clearer -// stacktraces. -#define PA_CONFIG_STARSCAN_NOINLINE_SCAN_FUNCTIONS() 1 - -// TODO(bikineev): Temporarily disable *Scan in MemoryReclaimer as it seems to -// cause significant jank. -#define PA_CONFIG_STARSCAN_ENABLE_STARSCAN_ON_RECLAIM() 0 - -// Double free detection comes with expensive cmpxchg (with the loop around it). -// We currently disable it to improve the runtime. -#define PA_CONFIG_STARSCAN_EAGER_DOUBLE_FREE_DETECTION_ENABLED() 0 - -// POSIX is not only UNIX, e.g. macOS and other OSes. We do use Linux-specific -// features such as futex(2). -#define PA_CONFIG_HAS_LINUX_KERNEL() \ - (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)) && !defined(STARBOARD) - -// On some platforms, we implement locking by spinning in userspace, then going -// into the kernel only if there is contention. This requires platform support, -// namely: -// - On Linux, futex(2) -// - On Windows, a fast userspace "try" operation which is available -// with SRWLock -// - On macOS, pthread_mutex_trylock() is fast by default starting with macOS -// 10.14. Chromium targets an earlier version, so it cannot be known at -// compile-time. So we use something different. -// - Otherwise, on POSIX we assume that a fast userspace pthread_mutex_trylock() -// is available. -// -// Otherwise, a userspace spinlock implementation is used. -#if PA_CONFIG(HAS_LINUX_KERNEL) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || \ - BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) -#define PA_CONFIG_HAS_FAST_MUTEX() 1 -#else -#define PA_CONFIG_HAS_FAST_MUTEX() 0 -#endif - -// If defined, enables zeroing memory on Free() with roughly 1% probability. -// This applies only to normal buckets, as direct-map allocations are always -// decommitted. -// TODO(bartekn): Re-enable once PartitionAlloc-Everywhere evaluation is done. -#define PA_CONFIG_ZERO_RANDOMLY_ON_FREE() 0 - -// Need TLS support. -#define PA_CONFIG_THREAD_CACHE_SUPPORTED() \ - (BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_FUCHSIA)) - -// Too expensive for official builds, as it adds cache misses to all -// allocations. On the other hand, we want wide metrics coverage to get -// realistic profiles. -#define PA_CONFIG_THREAD_CACHE_ALLOC_STATS() \ - (BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && !defined(OFFICIAL_BUILD)) - -// Optional statistics collection. Lightweight, contrary to the ones above, -// hence enabled by default. -#define PA_CONFIG_THREAD_CACHE_ENABLE_STATISTICS() 1 - -// Enable free list shadow entry to strengthen hardening as much as possible. -// The shadow entry is an inversion (bitwise-NOT) of the encoded `next` pointer. -// -// Disabled when ref-count is placed in the previous slot, as it will overlap -// with the shadow for the smallest slots. -// -// Disabled on Big Endian CPUs, because encoding is also a bitwise-NOT there, -// making the shadow entry equal to the original, valid pointer to the next -// slot. In case Use-after-Free happens, we'd rather not hand out a valid, -// ready-to-use pointer. -#define PA_CONFIG_HAS_FREELIST_SHADOW_ENTRY() \ - (!BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) && \ - defined(ARCH_CPU_LITTLE_ENDIAN)) - -#define PA_CONFIG_HAS_MEMORY_TAGGING() \ - (defined(ARCH_CPU_ARM64) && defined(__clang__) && \ - (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID))) - -#if PA_CONFIG(HAS_MEMORY_TAGGING) -static_assert(sizeof(void*) == 8); -#endif - -// Specifies whether allocation extras need to be added. -#if BUILDFLAG(PA_DCHECK_IS_ON) || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) -#define PA_CONFIG_EXTRAS_REQUIRED() 1 -#else -#define PA_CONFIG_EXTRAS_REQUIRED() 0 -#endif - -// Count and total wall clock time spent in memory related system calls. This -// doesn't cover all system calls, in particular the ones related to locking. -// -// Not enabled by default, as it has a runtime cost, and causes issues with some -// builds (e.g. Windows). -// However the total count is collected on all platforms. -#define PA_CONFIG_COUNT_SYSCALL_TIME() 0 - -// On Windows, |thread_local| variables cannot be marked "dllexport", see -// compiler error C2492 at -// https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-1/compiler-error-c2492?view=msvc-160. -// Don't use it there. -// -// On macOS and iOS: -// - With PartitionAlloc-Everywhere, thread_local allocates, reentering the -// allocator. -// - Component builds triggered a clang bug: crbug.com/1243375 -// -// Regardless, the "normal" TLS access is fast on x86_64 (see partition_tls.h), -// so don't bother with thread_local anywhere. -#define PA_CONFIG_THREAD_LOCAL_TLS() \ - (!(BUILDFLAG(IS_WIN) && defined(COMPONENT_BUILD)) && !BUILDFLAG(IS_APPLE)) - -// When PartitionAlloc is malloc(), detect malloc() becoming re-entrant by -// calling malloc() again. -// -// Limitations: -// - BUILDFLAG(PA_DCHECK_IS_ON) due to runtime cost -// - thread_local TLS to simplify the implementation -// - Not on Android due to bot failures -#if BUILDFLAG(PA_DCHECK_IS_ON) && BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \ - PA_CONFIG(THREAD_LOCAL_TLS) && !BUILDFLAG(IS_ANDROID) -#define PA_CONFIG_HAS_ALLOCATION_GUARD() 1 -#else -#define PA_CONFIG_HAS_ALLOCATION_GUARD() 0 -#endif - -// On Android, we have to go through emutls, since this is always a shared -// library, so don't bother. -#if PA_CONFIG(THREAD_LOCAL_TLS) && !BUILDFLAG(IS_ANDROID) -#define PA_CONFIG_THREAD_CACHE_FAST_TLS() 1 -#else -#define PA_CONFIG_THREAD_CACHE_FAST_TLS() 0 -#endif - -// Lazy commit should only be enabled on Windows, because commit charge is -// only meaningful and limited on Windows. It affects performance on other -// platforms and is simply not needed there due to OS supporting overcommit. -#if BUILDFLAG(IS_WIN) -constexpr bool kUseLazyCommit = true; -#else -constexpr bool kUseLazyCommit = false; -#endif - -// On these platforms, lock all the partitions before fork(), and unlock after. -// This may be required on more platforms in the future. -#define PA_CONFIG_HAS_ATFORK_HANDLER() \ - (BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) - -// PartitionAlloc uses PartitionRootEnumerator to acquire all -// PartitionRoots at BeforeFork and to release at AfterFork. -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && PA_CONFIG(HAS_ATFORK_HANDLER) -#define PA_CONFIG_USE_PARTITION_ROOT_ENUMERATOR() 1 -#else -#define PA_CONFIG_USE_PARTITION_ROOT_ENUMERATOR() 0 -#endif - -// Due to potential conflict with the free list pointer in the "previous slot" -// mode in the smallest bucket, we can't check both the cookie and the dangling -// raw_ptr at the same time. -#define PA_CONFIG_REF_COUNT_CHECK_COOKIE() \ - (!(BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) && \ - BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT)) && \ - (BUILDFLAG(PA_DCHECK_IS_ON) || \ - BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS))) - -// Use available space in the reference count to store the initially requested -// size from the application. This is used for debugging. On mac, it is used to -// workaround a bug. (crbug.com/1378822) -#if BUILDFLAG(IS_MAC) && !PA_CONFIG(REF_COUNT_CHECK_COOKIE) && \ - !BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) -#define PA_CONFIG_REF_COUNT_STORE_REQUESTED_SIZE() 1 -#else -#define PA_CONFIG_REF_COUNT_STORE_REQUESTED_SIZE() 0 -#endif - -#if PA_CONFIG(REF_COUNT_STORE_REQUESTED_SIZE) && \ - PA_CONFIG(REF_COUNT_CHECK_COOKIE) -#error "Cannot use a cookie *and* store the allocation size" -#endif - -// Prefer smaller slot spans. -// -// Smaller slot spans may improve dirty memory fragmentation, but may also -// increase address space usage. -// -// This is intended to roll out more broadly, but only enabled on Linux for now -// to get performance bot and real-world data pre-A/B experiment. -// -// Also enabled on ARM64 macOS, as the 16kiB pages on this platform lead to -// larger slot spans. -#define PA_CONFIG_PREFER_SMALLER_SLOT_SPANS() \ - (BUILDFLAG(IS_LINUX) || (BUILDFLAG(IS_MAC) && defined(ARCH_CPU_ARM64))) - -// Enable shadow metadata. -// -// With this flag, shadow pools will be mapped, on which writable shadow -// metadatas are placed, and the real metadatas are set to read-only instead. -// This feature is only enabled with 64-bit environment because pools work -// differently with 32-bits pointers (see glossary). -#if BUILDFLAG(ENABLE_SHADOW_METADATA_FOR_64_BITS_POINTERS) && \ - BUILDFLAG(HAS_64_BIT_POINTERS) -#define PA_CONFIG_ENABLE_SHADOW_METADATA() 1 -#else -#define PA_CONFIG_ENABLE_SHADOW_METADATA() 0 -#endif - -// According to crbug.com/1349955#c24, macOS 11 has a bug where they asset that -// malloc_size() of an allocation is equal to the requested size. This is -// generally not true. The assert passed only because it happened to be true for -// the sizes they requested. BRP changes that, hence can't be deployed without a -// workaround. -// -// The bug has been fixed in macOS 12. Here we can only check the platform, and -// the version is checked dynamically later. -#define PA_CONFIG_ENABLE_MAC11_MALLOC_SIZE_HACK() \ - (BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) && BUILDFLAG(IS_MAC)) - -// Enables compressed (4-byte) pointers that can point within the core pools -// (Regular + BRP). -#if BUILDFLAG(HAS_64_BIT_POINTERS) && BUILDFLAG(ENABLE_POINTER_COMPRESSION) -#define PA_CONFIG_POINTER_COMPRESSION() 1 - -#if !PA_CONFIG(GLUE_CORE_POOLS) -#error "Pointer compression works only with contiguous pools" -#endif -#if PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE) -#error "Dynamically selected pool size is currently not supported" -#endif -#if PA_CONFIG(HAS_MEMORY_TAGGING) -// TODO(1376980): Address MTE once it's enabled. -#error "Compressed pointers don't support tag in the upper bits" -#endif -#else // BUILDFLAG(HAS_64_BIT_POINTERS) && - // BUILDFLAG(ENABLE_POINTER_COMPRESSION) -#define PA_CONFIG_POINTER_COMPRESSION() 0 -#endif - -// PA_CONFIG(IS_NONCLANG_MSVC): mimics the compound condition used by -// Chromium's `//base/compiler_specific.h` to detect true (non-Clang) -// MSVC. -#if defined(COMPILER_MSVC) && !defined(__clang__) -#define PA_CONFIG_IS_NONCLANG_MSVC() 1 -#else -#define PA_CONFIG_IS_NONCLANG_MSVC() 0 -#endif - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_CONFIG_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_constants.h b/base/allocator/partition_allocator/partition_alloc_constants.h deleted file mode 100644 index b817a24..0000000 --- a/base/allocator/partition_allocator/partition_alloc_constants.h +++ /dev/null
@@ -1,505 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_CONSTANTS_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_CONSTANTS_H_ - -#include <algorithm> -#include <climits> -#include <cstddef> -#include <limits> - -#include "base/allocator/partition_allocator/address_pool_manager_types.h" -#include "base/allocator/partition_allocator/page_allocator_constants.h" -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_config.h" -#include "base/allocator/partition_allocator/partition_alloc_forward.h" -#include "base/allocator/partition_allocator/tagging.h" -#include "build/build_config.h" - -#if BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_64_BITS) -#include <mach/vm_page_size.h> -#endif - -namespace partition_alloc { - -// Bit flag constants used as `flag` argument of PartitionRoot::AllocWithFlags, -// AlignedAllocWithFlags, etc. -struct AllocFlags { - static constexpr unsigned int kReturnNull = 1 << 0; - static constexpr unsigned int kZeroFill = 1 << 1; - // Don't allow allocation override hooks. Override hooks are expected to - // check for the presence of this flag and return false if it is active. - static constexpr unsigned int kNoOverrideHooks = 1 << 2; - // Never let a memory tool like ASan (if active) perform the allocation. - static constexpr unsigned int kNoMemoryToolOverride = 1 << 3; - // Don't allow any hooks (override or observers). - static constexpr unsigned int kNoHooks = 1 << 4; // Internal. - // If the allocation requires a "slow path" (such as allocating/committing a - // new slot span), return nullptr instead. Note this makes all large - // allocations return nullptr, such as direct-mapped ones, and even for - // smaller ones, a nullptr value is common. - static constexpr unsigned int kFastPathOrReturnNull = 1 << 5; // Internal. - - static constexpr unsigned int kLastFlag = kFastPathOrReturnNull; -}; - -// Bit flag constants used as `flag` argument of PartitionRoot::FreeWithFlags. -struct FreeFlags { - // See AllocFlags::kNoMemoryToolOverride. - static constexpr unsigned int kNoMemoryToolOverride = 1 << 0; - - static constexpr unsigned int kLastFlag = kNoMemoryToolOverride; -}; - -namespace internal { - -// Size of a cache line. Not all CPUs in the world have a 64 bytes cache line -// size, but as of 2021, most do. This is in particular the case for almost all -// x86_64 and almost all ARM CPUs supported by Chromium. As this is used for -// static alignment, we cannot query the CPU at runtime to determine the actual -// alignment, so use 64 bytes everywhere. Since this is only used to avoid false -// sharing, getting this wrong only results in lower performance, not incorrect -// code. -constexpr size_t kPartitionCachelineSize = 64; - -// Underlying partition storage pages (`PartitionPage`s) are a power-of-2 size. -// It is typical for a `PartitionPage` to be based on multiple system pages. -// Most references to "page" refer to `PartitionPage`s. -// -// *Super pages* are the underlying system allocations we make. Super pages -// contain multiple partition pages and include space for a small amount of -// metadata per partition page. -// -// Inside super pages, we store *slot spans*. A slot span is a continguous range -// of one or more `PartitionPage`s that stores allocations of the same size. -// Slot span sizes are adjusted depending on the allocation size, to make sure -// the packing does not lead to unused (wasted) space at the end of the last -// system page of the span. For our current maximum slot span size of 64 KiB and -// other constant values, we pack _all_ `PartitionRoot::Alloc` sizes perfectly -// up against the end of a system page. - -#if defined(_MIPS_ARCH_LOONGSON) || defined(ARCH_CPU_LOONG64) -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t -PartitionPageShift() { - return 16; // 64 KiB -} -#elif defined(ARCH_CPU_PPC64) -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t -PartitionPageShift() { - return 18; // 256 KiB -} -#elif (BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_64_BITS)) || \ - (BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64)) -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t -PartitionPageShift() { - return PageAllocationGranularityShift() + 2; -} -#else -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t -PartitionPageShift() { - return 14; // 16 KiB -} -#endif -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t -PartitionPageSize() { - return 1 << PartitionPageShift(); -} -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t -PartitionPageOffsetMask() { - return PartitionPageSize() - 1; -} -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t -PartitionPageBaseMask() { - return ~PartitionPageOffsetMask(); -} - -// Number of system pages per regular slot span. Above this limit, we call it -// a single-slot span, as the span literally hosts only one slot, and has -// somewhat different implementation. At run-time, single-slot spans can be -// differentiated with a call to CanStoreRawSize(). -// TODO: Should this be 1 on platforms with page size larger than 4kB, e.g. -// ARM macOS or defined(_MIPS_ARCH_LOONGSON)? -constexpr size_t kMaxPartitionPagesPerRegularSlotSpan = 4; - -// To avoid fragmentation via never-used freelist entries, we hand out partition -// freelist sections gradually, in units of the dominant system page size. What -// we're actually doing is avoiding filling the full `PartitionPage` (16 KiB) -// with freelist pointers right away. Writing freelist pointers will fault and -// dirty a private page, which is very wasteful if we never actually store -// objects there. - -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t -NumSystemPagesPerPartitionPage() { - return PartitionPageSize() >> SystemPageShift(); -} - -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t -MaxSystemPagesPerRegularSlotSpan() { - return NumSystemPagesPerPartitionPage() * - kMaxPartitionPagesPerRegularSlotSpan; -} - -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t -MaxRegularSlotSpanSize() { - return kMaxPartitionPagesPerRegularSlotSpan << PartitionPageShift(); -} - -// The maximum size that is used in an alternate bucket distribution. After this -// threshold, we only have 1 slot per slot-span, so external fragmentation -// doesn't matter. So, using the alternate bucket distribution after this -// threshold has no benefit, and only increases internal fragmentation. -// -// We would like this to be |MaxRegularSlotSpanSize()| on all platforms, but -// this is not constexpr on all platforms, so on other platforms we hardcode it, -// even though this may be too low, e.g. on systems with a page size >4KiB. -constexpr size_t kHighThresholdForAlternateDistribution = -#if PAGE_ALLOCATOR_CONSTANTS_ARE_CONSTEXPR - MaxRegularSlotSpanSize(); -#else - 1 << 16; -#endif - -// We reserve virtual address space in 2 MiB chunks (aligned to 2 MiB as well). -// These chunks are called *super pages*. We do this so that we can store -// metadata in the first few pages of each 2 MiB-aligned section. This makes -// freeing memory very fast. 2 MiB size & alignment were chosen, because this -// virtual address block represents a full but single page table allocation on -// ARM, ia32 and x64, which may be slightly more performance&memory efficient. -// (Note, these super pages are backed by 4 KiB system pages and have nothing to -// do with OS concept of "huge pages"/"large pages", even though the size -// coincides.) -// -// The layout of the super page is as follows. The sizes below are the same for -// 32- and 64-bit platforms. -// -// +-----------------------+ -// | Guard page (4 KiB) | -// | Metadata page (4 KiB) | -// | Guard pages (8 KiB) | -// | Free Slot Bitmap | -// | *Scan State Bitmap | -// | Slot span | -// | Slot span | -// | ... | -// | Slot span | -// | Guard pages (16 KiB) | -// +-----------------------+ -// -// Free Slot Bitmap is only present when USE_FREESLOT_BITMAP is true. State -// Bitmap is inserted for partitions that may have quarantine enabled. -// -// If refcount_at_end_allocation is enabled, RefcountBitmap(4KiB) is inserted -// after the Metadata page for BackupRefPtr. The guard pages after the bitmap -// will be 4KiB. -// -//... -// | Metadata page (4 KiB) | -// | RefcountBitmap (4 KiB)| -// | Guard pages (4 KiB) | -//... -// -// Each slot span is a contiguous range of one or more `PartitionPage`s. Note -// that slot spans of different sizes may co-exist with one super page. Even -// slot spans of the same size may support different slot sizes. However, all -// slots within a span have to be of the same size. -// -// The metadata page has the following format. Note that the `PartitionPage` -// that is not at the head of a slot span is "unused" (by most part, it only -// stores the offset from the head page). In other words, the metadata for the -// slot span is stored only in the first `PartitionPage` of the slot span. -// Metadata accesses to other `PartitionPage`s are redirected to the first -// `PartitionPage`. -// -// +---------------------------------------------+ -// | SuperPageExtentEntry (32 B) | -// | PartitionPage of slot span 1 (32 B, used) | -// | PartitionPage of slot span 1 (32 B, unused) | -// | PartitionPage of slot span 1 (32 B, unused) | -// | PartitionPage of slot span 2 (32 B, used) | -// | PartitionPage of slot span 3 (32 B, used) | -// | ... | -// | PartitionPage of slot span N (32 B, used) | -// | PartitionPage of slot span N (32 B, unused) | -// | PartitionPage of slot span N (32 B, unused) | -// +---------------------------------------------+ -// -// A direct-mapped page has an identical layout at the beginning to fake it -// looking like a super page: -// -// +---------------------------------+ -// | Guard page (4 KiB) | -// | Metadata page (4 KiB) | -// | Guard pages (8 KiB) | -// | Direct mapped object | -// | Guard page (4 KiB, 32-bit only) | -// +---------------------------------+ -// -// A direct-mapped page's metadata page has the following layout (on 64 bit -// architectures. On 32 bit ones, the layout is identical, some sizes are -// different due to smaller pointers.): -// -// +----------------------------------+ -// | SuperPageExtentEntry (32 B) | -// | PartitionPage (32 B) | -// | PartitionBucket (40 B) | -// | PartitionDirectMapExtent (32 B) | -// +----------------------------------+ -// -// See |PartitionDirectMapMetadata| for details. - -constexpr size_t kGiB = 1024 * 1024 * 1024ull; -constexpr size_t kSuperPageShift = 21; // 2 MiB -constexpr size_t kSuperPageSize = 1 << kSuperPageShift; -constexpr size_t kSuperPageAlignment = kSuperPageSize; -constexpr size_t kSuperPageOffsetMask = kSuperPageAlignment - 1; -constexpr size_t kSuperPageBaseMask = ~kSuperPageOffsetMask; - -// PartitionAlloc's address space is split into pools. See `glossary.md`. - -enum pool_handle : unsigned { - kNullPoolHandle = 0u, - - kRegularPoolHandle, - kBRPPoolHandle, -#if BUILDFLAG(HAS_64_BIT_POINTERS) - kConfigurablePoolHandle, -#endif - -// New pool_handles will be added here. - -#if BUILDFLAG(ENABLE_PKEYS) - // The pkey pool must come last since we pkey_mprotect its entry in the - // metadata tables, e.g. AddressPoolManager::aligned_pools_ - kPkeyPoolHandle, -#endif - kMaxPoolHandle -}; - -// kNullPoolHandle doesn't have metadata, hence - 1 -constexpr size_t kNumPools = kMaxPoolHandle - 1; - -// Maximum pool size. With exception of Configurable Pool, it is also -// the actual size, unless PA_DYNAMICALLY_SELECT_POOL_SIZE is set, which -// allows to choose a different size at initialization time for certain -// configurations. -// -// Special-case Android and iOS, which incur test failures with larger -// pools. Regardless, allocating >8GiB with malloc() on these platforms is -// unrealistic as of 2022. -// -// When pointer compression is enabled, we cannot use large pools (at most -// 8GB for each of the glued pools). -#if BUILDFLAG(HAS_64_BIT_POINTERS) -#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS) || PA_CONFIG(POINTER_COMPRESSION) -constexpr size_t kPoolMaxSize = 8 * kGiB; -#else -constexpr size_t kPoolMaxSize = 16 * kGiB; -#endif -#else // BUILDFLAG(HAS_64_BIT_POINTERS) -constexpr size_t kPoolMaxSize = 4 * kGiB; -#endif -constexpr size_t kMaxSuperPagesInPool = kPoolMaxSize / kSuperPageSize; - -#if BUILDFLAG(ENABLE_PKEYS) -static_assert( - kPkeyPoolHandle == kNumPools, - "The pkey pool must come last since we pkey_mprotect its metadata."); -#endif - -// Slots larger than this size will not receive MTE protection. Pages intended -// for allocations larger than this constant should not be backed with PROT_MTE -// (which saves shadow tag memory). We also save CPU cycles by skipping tagging -// of large areas which are less likely to benefit from MTE protection. -// TODO(Richard.Townsend@arm.com): adjust RecommitSystemPagesForData to skip -// PROT_MTE. -constexpr size_t kMaxMemoryTaggingSize = 1024; - -#if PA_CONFIG(HAS_MEMORY_TAGGING) -// Returns whether the tag of |object| overflowed, meaning the containing slot -// needs to be moved to quarantine. -PA_ALWAYS_INLINE bool HasOverflowTag(void* object) { - // The tag with which the slot is put to quarantine. - constexpr uintptr_t kOverflowTag = 0x0f00000000000000uLL; - static_assert((kOverflowTag & kPtrTagMask) != 0, - "Overflow tag must be in tag bits"); - return (reinterpret_cast<uintptr_t>(object) & kPtrTagMask) == kOverflowTag; -} -#endif // PA_CONFIG(HAS_MEMORY_TAGGING) - -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t -NumPartitionPagesPerSuperPage() { - return kSuperPageSize >> PartitionPageShift(); -} - -PA_ALWAYS_INLINE constexpr size_t MaxSuperPagesInPool() { - return kMaxSuperPagesInPool; -} - -#if BUILDFLAG(HAS_64_BIT_POINTERS) -// In 64-bit mode, the direct map allocation granularity is super page size, -// because this is the reservation granularity of the pools. -PA_ALWAYS_INLINE constexpr size_t DirectMapAllocationGranularity() { - return kSuperPageSize; -} - -PA_ALWAYS_INLINE constexpr size_t DirectMapAllocationGranularityShift() { - return kSuperPageShift; -} -#else // BUILDFLAG(HAS_64_BIT_POINTERS) -// In 32-bit mode, address space is space is a scarce resource. Use the system -// allocation granularity, which is the lowest possible address space allocation -// unit. However, don't go below partition page size, so that pool bitmaps -// don't get too large. See kBytesPer1BitOfBRPPoolBitmap. -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t -DirectMapAllocationGranularity() { - return std::max(PageAllocationGranularity(), PartitionPageSize()); -} - -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t -DirectMapAllocationGranularityShift() { - return std::max(PageAllocationGranularityShift(), PartitionPageShift()); -} -#endif // BUILDFLAG(HAS_64_BIT_POINTERS) - -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t -DirectMapAllocationGranularityOffsetMask() { - return DirectMapAllocationGranularity() - 1; -} - -// The "order" of an allocation is closely related to the power-of-1 size of the -// allocation. More precisely, the order is the bit index of the -// most-significant-bit in the allocation size, where the bit numbers starts at -// index 1 for the least-significant-bit. -// -// In terms of allocation sizes, order 0 covers 0, order 1 covers 1, order 2 -// covers 2->3, order 3 covers 4->7, order 4 covers 8->15. - -// PartitionAlloc should return memory properly aligned for any type, to behave -// properly as a generic allocator. This is not strictly required as long as -// types are explicitly allocated with PartitionAlloc, but is to use it as a -// malloc() implementation, and generally to match malloc()'s behavior. -// -// In practice, this means 8 bytes alignment on 32 bit architectures, and 16 -// bytes on 64 bit ones. -// -// Keep in sync with //tools/memory/partition_allocator/objects_per_size_py. -constexpr size_t kMinBucketedOrder = - kAlignment == 16 ? 5 : 4; // 2^(order - 1), that is 16 or 8. -// The largest bucketed order is 1 << (20 - 1), storing [512 KiB, 1 MiB): -constexpr size_t kMaxBucketedOrder = 20; -constexpr size_t kNumBucketedOrders = - (kMaxBucketedOrder - kMinBucketedOrder) + 1; -// 8 buckets per order (for the higher orders). -// Note: this is not what is used by default, but the maximum amount of buckets -// per order. By default, only 4 are used. -constexpr size_t kNumBucketsPerOrderBits = 3; -constexpr size_t kNumBucketsPerOrder = 1 << kNumBucketsPerOrderBits; -constexpr size_t kNumBuckets = kNumBucketedOrders * kNumBucketsPerOrder; -constexpr size_t kSmallestBucket = 1 << (kMinBucketedOrder - 1); -constexpr size_t kMaxBucketSpacing = - 1 << ((kMaxBucketedOrder - 1) - kNumBucketsPerOrderBits); -constexpr size_t kMaxBucketed = (1 << (kMaxBucketedOrder - 1)) + - ((kNumBucketsPerOrder - 1) * kMaxBucketSpacing); -// Limit when downsizing a direct mapping using `realloc`: -constexpr size_t kMinDirectMappedDownsize = kMaxBucketed + 1; -// Intentionally set to less than 2GiB to make sure that a 2GiB allocation -// fails. This is a security choice in Chrome, to help making size_t vs int bugs -// harder to exploit. - -// The definition of MaxDirectMapped does only depend on constants that are -// unconditionally constexpr. Therefore it is not necessary to use -// PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR here. -PA_ALWAYS_INLINE constexpr size_t MaxDirectMapped() { - // Subtract kSuperPageSize to accommodate for granularity inside - // PartitionRoot::GetDirectMapReservationSize. - return (1UL << 31) - kSuperPageSize; -} - -// Max alignment supported by AlignedAllocWithFlags(). -// kSuperPageSize alignment can't be easily supported, because each super page -// starts with guard pages & metadata. -constexpr size_t kMaxSupportedAlignment = kSuperPageSize / 2; - -constexpr size_t kBitsPerSizeT = sizeof(void*) * CHAR_BIT; - -// When a SlotSpan becomes empty, the allocator tries to avoid re-using it -// immediately, to help with fragmentation. At this point, it becomes dirty -// committed memory, which we want to minimize. This could be decommitted -// immediately, but that would imply doing a lot of system calls. In particular, -// for single-slot SlotSpans, a malloc() / free() loop would cause a *lot* of -// system calls. -// -// As an intermediate step, empty SlotSpans are placed into a per-partition -// global ring buffer, giving the newly-empty SlotSpan a chance to be re-used -// before getting decommitted. A new entry (i.e. a newly empty SlotSpan) taking -// the place used by a previous one will lead the previous SlotSpan to be -// decommitted immediately, provided that it is still empty. -// -// Setting this value higher means giving more time for reuse to happen, at the -// cost of possibly increasing peak committed memory usage (and increasing the -// size of PartitionRoot a bit, since the ring buffer is there). Note that the -// ring buffer doesn't necessarily contain an empty SlotSpan, as SlotSpans are -// *not* removed from it when re-used. So the ring buffer really is a buffer of -// *possibly* empty SlotSpans. -// -// In all cases, PartitionRoot::PurgeMemory() with the -// PurgeFlags::kDecommitEmptySlotSpans flag will eagerly decommit all entries -// in the ring buffer, so with periodic purge enabled, this typically happens -// every few seconds. -constexpr size_t kEmptyCacheIndexBits = 7; -// kMaxFreeableSpans is the buffer size, but is never used as an index value, -// hence <= is appropriate. -constexpr size_t kMaxFreeableSpans = 1 << kEmptyCacheIndexBits; -constexpr size_t kDefaultEmptySlotSpanRingSize = 16; - -// If the total size in bytes of allocated but not committed pages exceeds this -// value (probably it is a "out of virtual address space" crash), a special -// crash stack trace is generated at -// `PartitionOutOfMemoryWithLotsOfUncommitedPages`. This is to distinguish "out -// of virtual address space" from "out of physical memory" in crash reports. -constexpr size_t kReasonableSizeOfUnusedPages = 1024 * 1024 * 1024; // 1 GiB - -// These byte values match tcmalloc. -constexpr unsigned char kUninitializedByte = 0xAB; -constexpr unsigned char kFreedByte = 0xCD; - -constexpr unsigned char kQuarantinedByte = 0xEF; - -// 1 is smaller than anything we can use, as it is not properly aligned. Not -// using a large size, since PartitionBucket::slot_size is a uint32_t, and -// static_cast<uint32_t>(-1) is too close to a "real" size. -constexpr size_t kInvalidBucketSize = 1; - -#if PA_CONFIG(ENABLE_MAC11_MALLOC_SIZE_HACK) -// Requested size that require the hack. -constexpr size_t kMac11MallocSizeHackRequestedSize = 32; -// Usable size for allocations that require the hack. -constexpr size_t kMac11MallocSizeHackUsableSize = -#if BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) || \ - PA_CONFIG(REF_COUNT_STORE_REQUESTED_SIZE) || \ - PA_CONFIG(REF_COUNT_CHECK_COOKIE) - 40; -#else - 44; -#endif // BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) || - // PA_CONFIG(REF_COUNT_STORE_REQUESTED_SIZE) || - // PA_CONFIG(REF_COUNT_CHECK_COOKIE) -#endif // PA_CONFIG(ENABLE_MAC11_MALLOC_SIZE_HACK) -} // namespace internal - -// These constants are used outside PartitionAlloc itself, so we provide -// non-internal aliases here. -using ::partition_alloc::internal::kInvalidBucketSize; -using ::partition_alloc::internal::kMaxSuperPagesInPool; -using ::partition_alloc::internal::kMaxSupportedAlignment; -using ::partition_alloc::internal::kNumBuckets; -using ::partition_alloc::internal::kSuperPageSize; -using ::partition_alloc::internal::MaxDirectMapped; -using ::partition_alloc::internal::PartitionPageSize; - -} // namespace partition_alloc - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_CONSTANTS_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_for_testing.h b/base/allocator/partition_allocator/partition_alloc_for_testing.h deleted file mode 100644 index 84da5e2..0000000 --- a/base/allocator/partition_allocator/partition_alloc_for_testing.h +++ /dev/null
@@ -1,50 +0,0 @@ -// 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. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_FOR_TESTING_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_FOR_TESTING_H_ - -#include "base/allocator/partition_allocator/partition_alloc.h" - -namespace partition_alloc { -namespace internal { - -constexpr bool AllowLeaks = true; -constexpr bool DisallowLeaks = false; - -// A subclass of PartitionAllocator for testing. It will free all resources, -// i.e. allocated memory, memory inside freelist, and so on, when destructing -// it or when manually invoking reset(). -// If need to check if there are any memory allocated but not freed yet, -// use allow_leaks=false. We will see CHECK failure inside reset() if any -// leak is detected. Otherwise (e.g. intentional leaks), use allow_leaks=true. -template <bool thread_safe, bool allow_leaks> -struct PartitionAllocatorForTesting : public PartitionAllocator<thread_safe> { - PartitionAllocatorForTesting() : PartitionAllocator<thread_safe>() {} - - explicit PartitionAllocatorForTesting(PartitionOptions opts) - : PartitionAllocator<thread_safe>() { - PartitionAllocator<thread_safe>::init(opts); - } - - ~PartitionAllocatorForTesting() { reset(); } - - PA_ALWAYS_INLINE void reset() { - PartitionAllocator<thread_safe>::root()->ResetForTesting(allow_leaks); - } -}; - -} // namespace internal - -using PartitionAllocatorForTesting = - internal::PartitionAllocatorForTesting<internal::ThreadSafe, - internal::DisallowLeaks>; - -using PartitionAllocatorAllowLeaksForTesting = - internal::PartitionAllocatorForTesting<internal::ThreadSafe, - internal::AllowLeaks>; - -} // namespace partition_alloc - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_FOR_TESTING_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_forward.h b/base/allocator/partition_allocator/partition_alloc_forward.h deleted file mode 100644 index e067f99..0000000 --- a/base/allocator/partition_allocator/partition_alloc_forward.h +++ /dev/null
@@ -1,97 +0,0 @@ -// 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 BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_FORWARD_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_FORWARD_H_ - -#include <algorithm> -#include <cstddef> -#include <cstdint> -#include <type_traits> - -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_config.h" - -namespace partition_alloc { - -namespace internal { - -// Alignment has two constraints: -// - Alignment requirement for scalar types: alignof(std::max_align_t) -// - Alignment requirement for operator new(). -// -// The two are separate on Windows 64 bits, where the first one is 8 bytes, and -// the second one 16. We could technically return something different for -// malloc() and operator new(), but this would complicate things, and most of -// our allocations are presumably coming from operator new() anyway. -constexpr size_t kAlignment = - std::max(alignof(max_align_t), - static_cast<size_t>(__STDCPP_DEFAULT_NEW_ALIGNMENT__)); -static_assert(kAlignment <= 16, - "PartitionAlloc doesn't support a fundamental alignment larger " - "than 16 bytes."); - -constexpr bool ThreadSafe = true; - -template <bool thread_safe> -struct SlotSpanMetadata; - -#if (BUILDFLAG(PA_DCHECK_IS_ON) || \ - BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS)) && \ - BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -void CheckThatSlotOffsetIsZero(uintptr_t address); -#endif - -// This type trait verifies a type can be used as a pointer offset. -// -// We support pointer offsets in signed (ptrdiff_t) or unsigned (size_t) values. -// Smaller types are also allowed. -template <typename Z> -static constexpr bool is_offset_type = - std::is_integral_v<Z> && sizeof(Z) <= sizeof(ptrdiff_t); - -} // namespace internal - -class PartitionStatsDumper; - -template <bool thread_safe = internal::ThreadSafe> -struct PartitionRoot; - -using ThreadSafePartitionRoot = PartitionRoot<internal::ThreadSafe>; - -} // namespace partition_alloc - -// From https://clang.llvm.org/docs/AttributeReference.html#malloc: -// -// The malloc attribute indicates that the function acts like a system memory -// allocation function, returning a pointer to allocated storage disjoint from -// the storage for any other object accessible to the caller. -// -// Note that it doesn't apply to realloc()-type functions, as they can return -// the same pointer as the one passed as a parameter, as noted in e.g. stdlib.h -// on Linux systems. -#if PA_HAS_ATTRIBUTE(malloc) -#define PA_MALLOC_FN __attribute__((malloc)) -#endif - -// Allows the compiler to assume that the return value is aligned on a -// kAlignment boundary. This is useful for e.g. using aligned vector -// instructions in the constructor for zeroing. -#if PA_HAS_ATTRIBUTE(assume_aligned) -#define PA_MALLOC_ALIGNED \ - __attribute__((assume_aligned(::partition_alloc::internal::kAlignment))) -#endif - -#if !defined(PA_MALLOC_FN) -#define PA_MALLOC_FN -#endif - -#if !defined(PA_MALLOC_ALIGNED) -#define PA_MALLOC_ALIGNED -#endif - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_FORWARD_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_hooks.cc b/base/allocator/partition_allocator/partition_alloc_hooks.cc deleted file mode 100644 index 0de4de4..0000000 --- a/base/allocator/partition_allocator/partition_alloc_hooks.cc +++ /dev/null
@@ -1,134 +0,0 @@ -// 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. - -#include "base/allocator/partition_allocator/partition_alloc_hooks.h" - -#include <ostream> - -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/partition_lock.h" - -namespace partition_alloc { - -namespace { - -internal::Lock g_hook_lock; - -internal::Lock& GetHooksLock() { - return g_hook_lock; -} - -} // namespace - -std::atomic<bool> PartitionAllocHooks::hooks_enabled_(false); -std::atomic<PartitionAllocHooks::AllocationObserverHook*> - PartitionAllocHooks::allocation_observer_hook_(nullptr); -std::atomic<PartitionAllocHooks::FreeObserverHook*> - PartitionAllocHooks::free_observer_hook_(nullptr); -std::atomic<PartitionAllocHooks::AllocationOverrideHook*> - PartitionAllocHooks::allocation_override_hook_(nullptr); -std::atomic<PartitionAllocHooks::FreeOverrideHook*> - PartitionAllocHooks::free_override_hook_(nullptr); -std::atomic<PartitionAllocHooks::ReallocOverrideHook*> - PartitionAllocHooks::realloc_override_hook_(nullptr); -std::atomic<PartitionAllocHooks::QuarantineOverrideHook*> - PartitionAllocHooks::quarantine_override_hook_(nullptr); - -void PartitionAllocHooks::SetObserverHooks(AllocationObserverHook* alloc_hook, - FreeObserverHook* free_hook) { - internal::ScopedGuard guard(GetHooksLock()); - - // Chained hooks are not supported. Registering a non-null hook when a - // non-null hook is already registered indicates somebody is trying to - // overwrite a hook. - PA_CHECK((!allocation_observer_hook_ && !free_observer_hook_) || - (!alloc_hook && !free_hook)) - << "Overwriting already set observer hooks"; - allocation_observer_hook_ = alloc_hook; - free_observer_hook_ = free_hook; - - hooks_enabled_ = allocation_observer_hook_ || allocation_override_hook_; -} - -void PartitionAllocHooks::SetOverrideHooks(AllocationOverrideHook* alloc_hook, - FreeOverrideHook* free_hook, - ReallocOverrideHook realloc_hook) { - internal::ScopedGuard guard(GetHooksLock()); - - PA_CHECK((!allocation_override_hook_ && !free_override_hook_ && - !realloc_override_hook_) || - (!alloc_hook && !free_hook && !realloc_hook)) - << "Overwriting already set override hooks"; - allocation_override_hook_ = alloc_hook; - free_override_hook_ = free_hook; - realloc_override_hook_ = realloc_hook; - - hooks_enabled_ = allocation_observer_hook_ || allocation_override_hook_; -} - -void PartitionAllocHooks::AllocationObserverHookIfEnabled( - void* address, - size_t size, - const char* type_name) { - if (auto* hook = allocation_observer_hook_.load(std::memory_order_relaxed)) { - hook(address, size, type_name); - } -} - -bool PartitionAllocHooks::AllocationOverrideHookIfEnabled( - void** out, - unsigned int flags, - size_t size, - const char* type_name) { - if (auto* hook = allocation_override_hook_.load(std::memory_order_relaxed)) { - return hook(out, flags, size, type_name); - } - return false; -} - -void PartitionAllocHooks::FreeObserverHookIfEnabled(void* address) { - if (auto* hook = free_observer_hook_.load(std::memory_order_relaxed)) { - hook(address); - } -} - -bool PartitionAllocHooks::FreeOverrideHookIfEnabled(void* address) { - if (auto* hook = free_override_hook_.load(std::memory_order_relaxed)) { - return hook(address); - } - return false; -} - -void PartitionAllocHooks::ReallocObserverHookIfEnabled(void* old_address, - void* new_address, - size_t size, - const char* type_name) { - // Report a reallocation as a free followed by an allocation. - AllocationObserverHook* allocation_hook = - allocation_observer_hook_.load(std::memory_order_relaxed); - FreeObserverHook* free_hook = - free_observer_hook_.load(std::memory_order_relaxed); - if (allocation_hook && free_hook) { - free_hook(old_address); - allocation_hook(new_address, size, type_name); - } -} - -bool PartitionAllocHooks::ReallocOverrideHookIfEnabled(size_t* out, - void* address) { - if (ReallocOverrideHook* hook = - realloc_override_hook_.load(std::memory_order_relaxed)) { - return hook(out, address); - } - return false; -} - -// Do not unset the hook if there are remaining quarantined slots -// not to break checks on unquarantining. -void PartitionAllocHooks::SetQuarantineOverrideHook( - QuarantineOverrideHook* hook) { - quarantine_override_hook_.store(hook, std::memory_order_release); -} - -} // namespace partition_alloc
diff --git a/base/allocator/partition_allocator/partition_alloc_hooks.h b/base/allocator/partition_allocator/partition_alloc_hooks.h deleted file mode 100644 index 8b97fd9..0000000 --- a/base/allocator/partition_allocator/partition_alloc_hooks.h +++ /dev/null
@@ -1,100 +0,0 @@ -// 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 BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_HOOKS_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_HOOKS_H_ - -#include <atomic> -#include <cstddef> - -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" - -namespace partition_alloc { - -// 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(void* address, - size_t size, - const char* type_name); - typedef void FreeObserverHook(void* address); - - // If it returns true, the allocation has been overridden with the pointer in - // *out. - typedef bool AllocationOverrideHook(void** out, - unsigned int 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(void* address, - size_t size, - const char* type_name); - static bool AllocationOverrideHookIfEnabled(void** out, - unsigned int flags, - size_t size, - const char* type_name); - - static void FreeObserverHookIfEnabled(void* address); - static bool FreeOverrideHookIfEnabled(void* address); - - static void ReallocObserverHookIfEnabled(void* old_address, - void* new_address, - size_t size, - const char* type_name); - 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 // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_HOOKS_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_notreached.h b/base/allocator/partition_allocator/partition_alloc_notreached.h deleted file mode 100644 index 4666642..0000000 --- a/base/allocator/partition_allocator/partition_alloc_notreached.h +++ /dev/null
@@ -1,55 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_NOTREACHED_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_NOTREACHED_H_ - -#include "base/allocator/partition_allocator/logging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" - -// When PartitionAlloc is used as the default allocator, we cannot use the -// regular (D)CHECK() macros, as they allocate internally. (c.f. // -// base/allocator/partition_allocator/partition_alloc_check.h) -// So PA_NOTREACHED() uses PA_DCHECK() instead of DCHECK(). - -#if BUILDFLAG(PA_ENABLE_LOG_ERROR_NOT_REACHED) -#define PA_NOTREACHED() \ - true ? ::partition_alloc::internal::logging::RawError( \ - __FILE__ "(" PA_STRINGIFY(__LINE__) ") PA_NOTREACHED() hit.") \ - : PA_EAT_CHECK_STREAM_PARAMS() - -#elif BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && defined(OFFICIAL_BUILD) && \ - defined(NDEBUG) && BUILDFLAG(PA_DCHECK_IS_ON) - -// PA_DCHECK(condition) is PA_CHECK(condition) if BUILDFLAG(PA_DCHECK_IS_ON). -// When BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC), OFFICIAL_BUILD, -// NDEBUG are defined, PA_CHECK(false) is IMMEDIATE_CRASH(). Since -// IMMEDIATE_CRASH() hints __builtin_unreachable() to the compiler, the -// following code causes compile failure: -// switch(...) { -// ... -// case X: -// PA_DCHECK(false); -// [[fallthrough]]; // The compiler knows "not reached". -// case Y: -// ... -// So define PA_NOTREACHED() by using async-signal-safe RawCheck(). -#define PA_NOTREACHED() \ - PA_UNLIKELY(true) \ - ? ::partition_alloc::internal::logging::RawCheck( \ - __FILE__ "(" PA_STRINGIFY(__LINE__) ") PA_NOTREACHED() hit.") \ - : PA_EAT_CHECK_STREAM_PARAMS() - -#else - -// PA_CHECK() uses RawCheck() for error reporting. So "PA_DCHECK(false); -// [[fallthrough]];" doesn't cause compile failure. -#define PA_NOTREACHED() PA_DCHECK(false) - -#endif - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_NOTREACHED_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_perftest.cc b/base/allocator/partition_allocator/partition_alloc_perftest.cc deleted file mode 100644 index c002aed..0000000 --- a/base/allocator/partition_allocator/partition_alloc_perftest.cc +++ /dev/null
@@ -1,528 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <algorithm> -#include <atomic> -#include <limits> -#include <memory> -#include <vector> - -#include "base/allocator/partition_allocator/extended_api.h" -#include "base/allocator/partition_allocator/partition_alloc.h" -#include "base/allocator/partition_allocator/partition_alloc_base/logging.h" -#include "base/allocator/partition_allocator/partition_alloc_base/strings/stringprintf.h" -#include "base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_for_testing.h" -#include "base/allocator/partition_allocator/partition_alloc_base/time/time.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/partition_alloc_for_testing.h" -#include "base/allocator/partition_allocator/thread_cache.h" -#include "base/debug/debugging_buildflags.h" -#include "base/timer/lap_timer.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/perf/perf_result_reporter.h" - -#if BUILDFLAG(IS_ANDROID) || defined(ARCH_CPU_32_BITS) || BUILDFLAG(IS_FUCHSIA) -// Some tests allocate many GB of memory, which can cause issues on Android and -// address-space exhaustion for any 32-bit process. -#define MEMORY_CONSTRAINED -#endif - -#if BUILDFLAG(ENABLE_ALLOCATION_STACK_TRACE_RECORDER) -#include "base/allocator/dispatcher/dispatcher.h" -#include "base/debug/allocation_trace.h" -#endif - -namespace partition_alloc::internal { - -namespace { - -// Change kTimeLimit to something higher if you need more time to capture a -// trace. -constexpr ::base::TimeDelta kTimeLimit = ::base::Seconds(2); -constexpr int kWarmupRuns = 10000; -constexpr int kTimeCheckInterval = 100000; -constexpr size_t kAllocSize = 40; - -// Size constants are mostly arbitrary, but try to simulate something like CSS -// parsing which consists of lots of relatively small objects. -constexpr int kMultiBucketMinimumSize = 24; -constexpr int kMultiBucketIncrement = 13; -// Final size is 24 + (13 * 22) = 310 bytes. -constexpr int kMultiBucketRounds = 22; - -constexpr char kMetricPrefixMemoryAllocation[] = "MemoryAllocation."; -constexpr char kMetricThroughput[] = "throughput"; -constexpr char kMetricTimePerAllocation[] = "time_per_allocation"; - -perf_test::PerfResultReporter SetUpReporter(const std::string& story_name) { - perf_test::PerfResultReporter reporter(kMetricPrefixMemoryAllocation, - story_name); - reporter.RegisterImportantMetric(kMetricThroughput, "runs/s"); - reporter.RegisterImportantMetric(kMetricTimePerAllocation, "ns"); - return reporter; -} - -enum class AllocatorType { - kSystem, - kPartitionAlloc, - kPartitionAllocWithThreadCache, -#if BUILDFLAG(ENABLE_ALLOCATION_STACK_TRACE_RECORDER) - kPartitionAllocWithAllocationStackTraceRecorder, -#endif -}; - -class Allocator { - public: - Allocator() = default; - virtual ~Allocator() = default; - virtual void* Alloc(size_t size) = 0; - virtual void Free(void* data) = 0; -}; - -class SystemAllocator : public Allocator { - public: - SystemAllocator() = default; - ~SystemAllocator() override = default; - void* Alloc(size_t size) override { return malloc(size); } - void Free(void* data) override { free(data); } -}; - -class PartitionAllocator : public Allocator { - public: - PartitionAllocator() = default; - ~PartitionAllocator() override { alloc_.DestructForTesting(); } - - void* Alloc(size_t size) override { - return alloc_.AllocWithFlagsNoHooks(0, size, PartitionPageSize()); - } - void Free(void* data) override { ThreadSafePartitionRoot::FreeNoHooks(data); } - - private: - ThreadSafePartitionRoot alloc_{{ - PartitionOptions::AlignedAlloc::kDisallowed, - PartitionOptions::ThreadCache::kDisabled, - PartitionOptions::Quarantine::kDisallowed, - PartitionOptions::Cookie::kAllowed, - PartitionOptions::BackupRefPtr::kDisabled, - PartitionOptions::BackupRefPtrZapping::kDisabled, - PartitionOptions::UseConfigurablePool::kNo, - }}; -}; - -class PartitionAllocatorWithThreadCache : public Allocator { - public: - explicit PartitionAllocatorWithThreadCache(bool use_alternate_bucket_dist) - : scope_(allocator_.root()) { - ThreadCacheRegistry::Instance().PurgeAll(); - if (!use_alternate_bucket_dist) { - allocator_.root()->SwitchToDenserBucketDistribution(); - } else { - allocator_.root()->ResetBucketDistributionForTesting(); - } - } - ~PartitionAllocatorWithThreadCache() override = default; - - void* Alloc(size_t size) override { - return allocator_.root()->AllocWithFlagsNoHooks(0, size, - PartitionPageSize()); - } - void Free(void* data) override { allocator_.root()->Free(data); } - - private: - static constexpr partition_alloc::PartitionOptions kOpts = { - PartitionOptions::AlignedAlloc::kDisallowed, -#if !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - PartitionOptions::ThreadCache::kEnabled, -#else - PartitionOptions::ThreadCache::kDisabled, -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - PartitionOptions::Quarantine::kDisallowed, - PartitionOptions::Cookie::kAllowed, - PartitionOptions::BackupRefPtr::kDisabled, - PartitionOptions::BackupRefPtrZapping::kDisabled, - PartitionOptions::UseConfigurablePool::kNo, - }; - PartitionAllocatorForTesting<internal::ThreadSafe, internal::DisallowLeaks> - allocator_{kOpts}; - internal::ThreadCacheProcessScopeForTesting scope_; -}; - -#if BUILDFLAG(ENABLE_ALLOCATION_STACK_TRACE_RECORDER) -class PartitionAllocatorWithAllocationStackTraceRecorder : public Allocator { - public: - explicit PartitionAllocatorWithAllocationStackTraceRecorder( - bool register_hooks) - : register_hooks_(register_hooks) { - if (register_hooks_) { - dispatcher_.InitializeForTesting(&recorder_); - } - } - - ~PartitionAllocatorWithAllocationStackTraceRecorder() override { - if (register_hooks_) { - dispatcher_.ResetForTesting(); - } - } - - void* Alloc(size_t size) override { - return alloc_.AllocWithFlags(0, size, nullptr); - } - - void Free(void* data) override { ThreadSafePartitionRoot::Free(data); } - - private: - bool const register_hooks_; - ThreadSafePartitionRoot alloc_{{ - PartitionOptions::AlignedAlloc::kDisallowed, - PartitionOptions::ThreadCache::kDisabled, - PartitionOptions::Quarantine::kDisallowed, - PartitionOptions::Cookie::kAllowed, - PartitionOptions::BackupRefPtr::kDisabled, - PartitionOptions::BackupRefPtrZapping::kDisabled, - PartitionOptions::UseConfigurablePool::kNo, - }}; - ::base::allocator::dispatcher::Dispatcher& dispatcher_ = - ::base::allocator::dispatcher::Dispatcher::GetInstance(); - ::base::debug::tracer::AllocationTraceRecorder recorder_; -}; -#endif // BUILDFLAG(ENABLE_ALLOCATION_STACK_TRACE_RECORDER) - -class TestLoopThread : public base::PlatformThreadForTesting::Delegate { - public: - TestLoopThread(float (*test_fn)(Allocator*), Allocator* allocator) - : test_fn_(test_fn), allocator_(allocator) { - PA_CHECK(base::PlatformThreadForTesting::Create(0, this, &thread_handle_)); - } - - float Run() { - base::PlatformThreadForTesting::Join(thread_handle_); - return laps_per_second_; - } - - void ThreadMain() override { laps_per_second_ = test_fn_(allocator_); } - - float (*test_fn_)(Allocator*) = nullptr; - Allocator* allocator_ = nullptr; - base::PlatformThreadHandle thread_handle_; - std::atomic<float> laps_per_second_; -}; - -void DisplayResults(const std::string& story_name, - float iterations_per_second) { - auto reporter = SetUpReporter(story_name); - reporter.AddResult(kMetricThroughput, iterations_per_second); - reporter.AddResult(kMetricTimePerAllocation, - static_cast<size_t>(1e9 / iterations_per_second)); -} - -class MemoryAllocationPerfNode { - public: - MemoryAllocationPerfNode* GetNext() const { return next_; } - void SetNext(MemoryAllocationPerfNode* p) { next_ = p; } - static void FreeAll(MemoryAllocationPerfNode* first, Allocator* alloc) { - MemoryAllocationPerfNode* cur = first; - while (cur != nullptr) { - MemoryAllocationPerfNode* next = cur->GetNext(); - alloc->Free(cur); - cur = next; - } - } - - private: - MemoryAllocationPerfNode* next_ = nullptr; -}; - -#if !defined(MEMORY_CONSTRAINED) -float SingleBucket(Allocator* allocator) { - auto* first = - reinterpret_cast<MemoryAllocationPerfNode*>(allocator->Alloc(kAllocSize)); - size_t allocated_memory = kAllocSize; - - ::base::LapTimer timer(kWarmupRuns, kTimeLimit, kTimeCheckInterval); - MemoryAllocationPerfNode* cur = first; - do { - auto* next = reinterpret_cast<MemoryAllocationPerfNode*>( - allocator->Alloc(kAllocSize)); - PA_CHECK(next != nullptr); - cur->SetNext(next); - cur = next; - timer.NextLap(); - allocated_memory += kAllocSize; - // With multiple threads, can get OOM otherwise. - if (allocated_memory > 200e6) { - cur->SetNext(nullptr); - MemoryAllocationPerfNode::FreeAll(first->GetNext(), allocator); - cur = first; - allocated_memory = kAllocSize; - } - } while (!timer.HasTimeLimitExpired()); - - // next_ = nullptr only works if the class constructor is called (it's not - // called in this case because then we can allocate arbitrary-length - // payloads.) - cur->SetNext(nullptr); - MemoryAllocationPerfNode::FreeAll(first, allocator); - - return timer.LapsPerSecond(); -} -#endif // defined(MEMORY_CONSTRAINED) - -float SingleBucketWithFree(Allocator* allocator) { - // Allocate an initial element to make sure the bucket stays set up. - void* elem = allocator->Alloc(kAllocSize); - - ::base::LapTimer timer(kWarmupRuns, kTimeLimit, kTimeCheckInterval); - do { - void* cur = allocator->Alloc(kAllocSize); - PA_CHECK(cur != nullptr); - allocator->Free(cur); - timer.NextLap(); - } while (!timer.HasTimeLimitExpired()); - - allocator->Free(elem); - return timer.LapsPerSecond(); -} - -#if !defined(MEMORY_CONSTRAINED) -float MultiBucket(Allocator* allocator) { - auto* first = - reinterpret_cast<MemoryAllocationPerfNode*>(allocator->Alloc(kAllocSize)); - MemoryAllocationPerfNode* cur = first; - size_t allocated_memory = kAllocSize; - - ::base::LapTimer timer(kWarmupRuns, kTimeLimit, kTimeCheckInterval); - do { - for (int i = 0; i < kMultiBucketRounds; i++) { - size_t size = kMultiBucketMinimumSize + (i * kMultiBucketIncrement); - auto* next = - reinterpret_cast<MemoryAllocationPerfNode*>(allocator->Alloc(size)); - PA_CHECK(next != nullptr); - cur->SetNext(next); - cur = next; - allocated_memory += size; - } - - // Can OOM with multiple threads. - if (allocated_memory > 100e6) { - cur->SetNext(nullptr); - MemoryAllocationPerfNode::FreeAll(first->GetNext(), allocator); - cur = first; - allocated_memory = kAllocSize; - } - - timer.NextLap(); - } while (!timer.HasTimeLimitExpired()); - - cur->SetNext(nullptr); - MemoryAllocationPerfNode::FreeAll(first, allocator); - - return timer.LapsPerSecond() * kMultiBucketRounds; -} -#endif // defined(MEMORY_CONSTRAINED) - -float MultiBucketWithFree(Allocator* allocator) { - std::vector<void*> elems; - elems.reserve(kMultiBucketRounds); - // Do an initial round of allocation to make sure that the buckets stay in - // use (and aren't accidentally released back to the OS). - for (int i = 0; i < kMultiBucketRounds; i++) { - void* cur = - allocator->Alloc(kMultiBucketMinimumSize + (i * kMultiBucketIncrement)); - PA_CHECK(cur != nullptr); - elems.push_back(cur); - } - - ::base::LapTimer timer(kWarmupRuns, kTimeLimit, kTimeCheckInterval); - do { - for (int i = 0; i < kMultiBucketRounds; i++) { - void* cur = allocator->Alloc(kMultiBucketMinimumSize + - (i * kMultiBucketIncrement)); - PA_CHECK(cur != nullptr); - allocator->Free(cur); - } - timer.NextLap(); - } while (!timer.HasTimeLimitExpired()); - - for (void* ptr : elems) { - allocator->Free(ptr); - } - - return timer.LapsPerSecond() * kMultiBucketRounds; -} - -float DirectMapped(Allocator* allocator) { - constexpr size_t kSize = 2 * 1000 * 1000; - - ::base::LapTimer timer(kWarmupRuns, kTimeLimit, kTimeCheckInterval); - do { - void* cur = allocator->Alloc(kSize); - PA_CHECK(cur != nullptr); - allocator->Free(cur); - timer.NextLap(); - } while (!timer.HasTimeLimitExpired()); - - return timer.LapsPerSecond(); -} - -std::unique_ptr<Allocator> CreateAllocator(AllocatorType type, - bool use_alternate_bucket_dist) { - switch (type) { - case AllocatorType::kSystem: - return std::make_unique<SystemAllocator>(); - case AllocatorType::kPartitionAlloc: - return std::make_unique<PartitionAllocator>(); - case AllocatorType::kPartitionAllocWithThreadCache: - return std::make_unique<PartitionAllocatorWithThreadCache>( - use_alternate_bucket_dist); -#if BUILDFLAG(ENABLE_ALLOCATION_STACK_TRACE_RECORDER) - case AllocatorType::kPartitionAllocWithAllocationStackTraceRecorder: - return std::make_unique< - PartitionAllocatorWithAllocationStackTraceRecorder>(true); -#endif - } -} - -void LogResults(int thread_count, - AllocatorType alloc_type, - uint64_t total_laps_per_second, - uint64_t min_laps_per_second) { - PA_LOG(INFO) << "RESULTSCSV: " << thread_count << "," - << static_cast<int>(alloc_type) << "," << total_laps_per_second - << "," << min_laps_per_second; -} - -void RunTest(int thread_count, - bool use_alternate_bucket_dist, - AllocatorType alloc_type, - float (*test_fn)(Allocator*), - float (*noisy_neighbor_fn)(Allocator*), - const char* story_base_name) { - auto alloc = CreateAllocator(alloc_type, use_alternate_bucket_dist); - - std::unique_ptr<TestLoopThread> noisy_neighbor_thread = nullptr; - if (noisy_neighbor_fn) { - noisy_neighbor_thread = - std::make_unique<TestLoopThread>(noisy_neighbor_fn, alloc.get()); - } - - std::vector<std::unique_ptr<TestLoopThread>> threads; - for (int i = 0; i < thread_count; ++i) { - threads.push_back(std::make_unique<TestLoopThread>(test_fn, alloc.get())); - } - - uint64_t total_laps_per_second = 0; - uint64_t min_laps_per_second = std::numeric_limits<uint64_t>::max(); - for (int i = 0; i < thread_count; ++i) { - uint64_t laps_per_second = threads[i]->Run(); - min_laps_per_second = std::min(laps_per_second, min_laps_per_second); - total_laps_per_second += laps_per_second; - } - - if (noisy_neighbor_thread) { - noisy_neighbor_thread->Run(); - } - - char const* alloc_type_str; - switch (alloc_type) { - case AllocatorType::kSystem: - alloc_type_str = "System"; - break; - case AllocatorType::kPartitionAlloc: - alloc_type_str = "PartitionAlloc"; - break; - case AllocatorType::kPartitionAllocWithThreadCache: - alloc_type_str = "PartitionAllocWithThreadCache"; - break; -#if BUILDFLAG(ENABLE_ALLOCATION_STACK_TRACE_RECORDER) - case AllocatorType::kPartitionAllocWithAllocationStackTraceRecorder: - alloc_type_str = "PartitionAllocWithAllocationStackTraceRecorder"; - break; -#endif - } - - std::string name = base::TruncatingStringPrintf( - "%s%s_%s_%d", kMetricPrefixMemoryAllocation, story_base_name, - alloc_type_str, thread_count); - - DisplayResults(name + "_total", total_laps_per_second); - DisplayResults(name + "_worst", min_laps_per_second); - LogResults(thread_count, alloc_type, total_laps_per_second, - min_laps_per_second); -} - -class PartitionAllocMemoryAllocationPerfTest - : public testing::TestWithParam<std::tuple<int, bool, AllocatorType>> {}; - -// Only one partition with a thread cache: cannot use the thread cache when -// PartitionAlloc is malloc(). -INSTANTIATE_TEST_SUITE_P( - , - PartitionAllocMemoryAllocationPerfTest, - ::testing::Combine( - ::testing::Values(1, 2, 3, 4), - ::testing::Values(false, true), - ::testing::Values( - AllocatorType::kSystem, - AllocatorType::kPartitionAlloc, - AllocatorType::kPartitionAllocWithThreadCache -#if BUILDFLAG(ENABLE_ALLOCATION_STACK_TRACE_RECORDER) - , - AllocatorType::kPartitionAllocWithAllocationStackTraceRecorder -#endif - ))); - -// This test (and the other one below) allocates a large amount of memory, which -// can cause issues on Android. -#if !defined(MEMORY_CONSTRAINED) -TEST_P(PartitionAllocMemoryAllocationPerfTest, SingleBucket) { - auto params = GetParam(); - RunTest(std::get<int>(params), std::get<bool>(params), - std::get<AllocatorType>(params), SingleBucket, nullptr, - "SingleBucket"); -} -#endif // defined(MEMORY_CONSTRAINED) - -TEST_P(PartitionAllocMemoryAllocationPerfTest, SingleBucketWithFree) { - auto params = GetParam(); - RunTest(std::get<int>(params), std::get<bool>(params), - std::get<AllocatorType>(params), SingleBucketWithFree, nullptr, - "SingleBucketWithFree"); -} - -#if !defined(MEMORY_CONSTRAINED) -TEST_P(PartitionAllocMemoryAllocationPerfTest, MultiBucket) { - auto params = GetParam(); - RunTest(std::get<int>(params), std::get<bool>(params), - std::get<AllocatorType>(params), MultiBucket, nullptr, "MultiBucket"); -} -#endif // defined(MEMORY_CONSTRAINED) - -TEST_P(PartitionAllocMemoryAllocationPerfTest, MultiBucketWithFree) { - auto params = GetParam(); - RunTest(std::get<int>(params), std::get<bool>(params), - std::get<AllocatorType>(params), MultiBucketWithFree, nullptr, - "MultiBucketWithFree"); -} - -TEST_P(PartitionAllocMemoryAllocationPerfTest, DirectMapped) { - auto params = GetParam(); - RunTest(std::get<int>(params), std::get<bool>(params), - std::get<AllocatorType>(params), DirectMapped, nullptr, - "DirectMapped"); -} - -#if !defined(MEMORY_CONSTRAINED) -TEST_P(PartitionAllocMemoryAllocationPerfTest, - DISABLED_MultiBucketWithNoisyNeighbor) { - auto params = GetParam(); - RunTest(std::get<int>(params), std::get<bool>(params), - std::get<AllocatorType>(params), MultiBucket, DirectMapped, - "MultiBucketWithNoisyNeighbor"); -} -#endif // !defined(MEMORY_CONSTRAINED) - -} // namespace - -} // namespace partition_alloc::internal
diff --git a/base/allocator/partition_allocator/partition_alloc_unittest.cc b/base/allocator/partition_allocator/partition_alloc_unittest.cc deleted file mode 100644 index 9a0db55..0000000 --- a/base/allocator/partition_allocator/partition_alloc_unittest.cc +++ /dev/null
@@ -1,5440 +0,0 @@ -// Copyright 2013 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_alloc_for_testing.h" - -#include <algorithm> -#include <cstddef> -#include <cstdint> -#include <cstdlib> -#include <cstring> -#include <iostream> -#include <limits> -#include <memory> -#include <random> -#include <set> -#include <tuple> -#include <vector> - -#include "base/allocator/partition_allocator/address_space_randomization.h" -#include "base/allocator/partition_allocator/chromecast_buildflags.h" -#include "base/allocator/partition_allocator/dangling_raw_ptr_checks.h" -#include "base/allocator/partition_allocator/freeslot_bitmap.h" -#include "base/allocator/partition_allocator/page_allocator_constants.h" -#include "base/allocator/partition_allocator/partition_address_space.h" -#include "base/allocator/partition_allocator/partition_alloc_base/bits.h" -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/cpu.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_base/logging.h" -#include "base/allocator/partition_allocator/partition_alloc_base/numerics/checked_math.h" -#include "base/allocator/partition_allocator/partition_alloc_base/rand_util.h" -#include "base/allocator/partition_allocator/partition_alloc_base/thread_annotations.h" -#include "base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_for_testing.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_config.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "base/allocator/partition_allocator/partition_bucket.h" -#include "base/allocator/partition_allocator/partition_cookie.h" -#include "base/allocator/partition_allocator/partition_freelist_entry.h" -#include "base/allocator/partition_allocator/partition_page.h" -#include "base/allocator/partition_allocator/partition_ref_count.h" -#include "base/allocator/partition_allocator/partition_root.h" -#include "base/allocator/partition_allocator/pkey.h" -#include "base/allocator/partition_allocator/reservation_offset_table.h" -#include "base/allocator/partition_allocator/tagging.h" -#include "base/system/sys_info.h" -#include "base/test/gtest_util.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -#if defined(__ARM_FEATURE_MEMORY_TAGGING) -#include <arm_acle.h> -#endif - -#if defined(STARBOARD) -#elif BUILDFLAG(IS_POSIX) -#if BUILDFLAG(IS_LINUX) -// We need PKEY_DISABLE_WRITE in this file; glibc defines it in sys/mman.h but -// it's actually Linux-specific and other Linux libcs define it in linux/mman.h. -// We have to include both to be sure we get the definition. -#include <linux/mman.h> -#endif // BUILDFLAG(IS_LINUX) -#include <sys/mman.h> -#include <sys/resource.h> -#include <sys/time.h> -#endif // BUILDFLAG(IS_POSIX) - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && BUILDFLAG(IS_MAC) -#include <OpenCL/opencl.h> -#endif - -#if BUILDFLAG(ENABLE_PKEYS) -#include <sys/syscall.h> -#endif - -// In the MTE world, the upper bits of a pointer can be decorated with a tag, -// thus allowing many versions of the same pointer to exist. These macros take -// that into account when comparing. -#define PA_EXPECT_PTR_EQ(ptr1, ptr2) \ - { EXPECT_EQ(UntagPtr(ptr1), UntagPtr(ptr2)); } -#define PA_EXPECT_PTR_NE(ptr1, ptr2) \ - { EXPECT_NE(UntagPtr(ptr1), UntagPtr(ptr2)); } - -#if !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - -namespace { - -bool IsLargeMemoryDevice() { - // Treat any device with 4GiB or more of physical memory as a "large memory - // device". We check for slightly less than GiB so that devices with a small - // amount of memory not accessible to the OS still count as "large". - // - // Set to 4GiB, since we have 2GiB Android devices where tests flakily fail - // (e.g. Nexus 5X, crbug.com/1191195). - return base::SysInfo::AmountOfPhysicalMemory() >= 4000ULL * 1024 * 1024; -} - -bool SetAddressSpaceLimit() { -#if !defined(ARCH_CPU_64_BITS) || !BUILDFLAG(IS_POSIX) - // 32 bits => address space is limited already. - return true; -#elif BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) - // macOS will accept, but not enforce, |RLIMIT_AS| changes. See - // https://crbug.com/435269 and rdar://17576114. - // - // Note: This number must be not less than 6 GB, because with - // sanitizer_coverage_flags=edge, it reserves > 5 GB of address space. See - // https://crbug.com/674665. - const size_t kAddressSpaceLimit = static_cast<size_t>(6144) * 1024 * 1024; - struct rlimit limit; - if (getrlimit(RLIMIT_DATA, &limit) != 0) { - return false; - } - if (limit.rlim_cur == RLIM_INFINITY || limit.rlim_cur > kAddressSpaceLimit) { - limit.rlim_cur = kAddressSpaceLimit; - if (setrlimit(RLIMIT_DATA, &limit) != 0) { - return false; - } - } - return true; -#else - return false; -#endif -} - -bool ClearAddressSpaceLimit() { -#if !defined(ARCH_CPU_64_BITS) || !BUILDFLAG(IS_POSIX) - return true; -#elif BUILDFLAG(IS_POSIX) - struct rlimit limit; - if (getrlimit(RLIMIT_DATA, &limit) != 0) { - return false; - } - limit.rlim_cur = limit.rlim_max; - if (setrlimit(RLIMIT_DATA, &limit) != 0) { - return false; - } - return true; -#else - return false; -#endif -} - -const size_t kTestSizes[] = { - 1, - 17, - 100, - partition_alloc::internal::SystemPageSize(), - partition_alloc::internal::SystemPageSize() + 1, - partition_alloc::PartitionRoot< - partition_alloc::internal::ThreadSafe>::GetDirectMapSlotSize(100), - 1 << 20, - 1 << 21, -}; -constexpr size_t kTestSizesCount = std::size(kTestSizes); - -void AllocateRandomly( - partition_alloc::PartitionRoot<partition_alloc::internal::ThreadSafe>* root, - size_t count, - unsigned int flags) { - std::vector<void*> allocations(count, nullptr); - for (size_t i = 0; i < count; ++i) { - const size_t size = - kTestSizes[partition_alloc::internal::base::RandGenerator( - kTestSizesCount)]; - allocations[i] = root->AllocWithFlags(flags, size, nullptr); - EXPECT_NE(nullptr, allocations[i]) << " size: " << size << " i: " << i; - } - - for (size_t i = 0; i < count; ++i) { - if (allocations[i]) { - root->Free(allocations[i]); - } - } -} - -void HandleOOM(size_t unused_size) { - PA_LOG(FATAL) << "Out of memory"; -} - -int g_dangling_raw_ptr_detected_count = 0; -int g_dangling_raw_ptr_released_count = 0; - -class CountDanglingRawPtr { - public: - CountDanglingRawPtr() { - g_dangling_raw_ptr_detected_count = 0; - g_dangling_raw_ptr_released_count = 0; - old_detected_fn_ = partition_alloc::GetDanglingRawPtrDetectedFn(); - old_released_fn_ = partition_alloc::GetDanglingRawPtrReleasedFn(); - - partition_alloc::SetDanglingRawPtrDetectedFn( - CountDanglingRawPtr::DanglingRawPtrDetected); - partition_alloc::SetDanglingRawPtrReleasedFn( - CountDanglingRawPtr::DanglingRawPtrReleased); - } - ~CountDanglingRawPtr() { - partition_alloc::SetDanglingRawPtrDetectedFn(old_detected_fn_); - partition_alloc::SetDanglingRawPtrReleasedFn(old_released_fn_); - } - - private: - static void DanglingRawPtrDetected(uintptr_t) { - g_dangling_raw_ptr_detected_count++; - } - static void DanglingRawPtrReleased(uintptr_t) { - g_dangling_raw_ptr_released_count++; - } - - partition_alloc::DanglingRawPtrDetectedFn* old_detected_fn_; - partition_alloc::DanglingRawPtrReleasedFn* old_released_fn_; -}; - -} // namespace - -// Note: This test exercises interfaces inside the `partition_alloc` -// namespace, but inspects objects inside `partition_alloc::internal`. -// For ease of reading, the tests are placed into the latter namespace. -namespace partition_alloc::internal { - -using BucketDistribution = ThreadSafePartitionRoot::BucketDistribution; -using SlotSpan = SlotSpanMetadata<ThreadSafe>; - -const size_t kTestAllocSize = 16; - -#if !BUILDFLAG(PA_DCHECK_IS_ON) -const size_t kPointerOffset = kPartitionRefCountOffsetAdjustment; -const size_t kExtraAllocSizeWithoutRefCount = 0ull; -#else -const size_t kPointerOffset = kPartitionRefCountOffsetAdjustment; -const size_t kExtraAllocSizeWithoutRefCount = kCookieSize; -#endif - -const size_t kExtraAllocSizeWithRefCount = - kExtraAllocSizeWithoutRefCount + kInSlotRefCountBufferSize; - -const char* type_name = nullptr; - -void SetDistributionForPartitionRoot(ThreadSafePartitionRoot* root, - BucketDistribution distribution) { - switch (distribution) { - case BucketDistribution::kDefault: - root->ResetBucketDistributionForTesting(); - break; - case BucketDistribution::kDenser: - root->SwitchToDenserBucketDistribution(); - break; - } -} - -size_t ExtraAllocSize( - const PartitionAllocator<internal::ThreadSafe>& allocator) { - return kExtraAllocSizeWithoutRefCount + - (allocator.root()->brp_enabled() ? kInSlotRefCountBufferSize : 0); -} - -class ScopedPageAllocation { - public: - ScopedPageAllocation(PartitionAllocator<internal::ThreadSafe>& allocator, - base::CheckedNumeric<size_t> npages) - : allocator_(allocator), - npages_(npages), - ptr_(static_cast<char*>(allocator_.root()->Alloc( - (npages * SystemPageSize() - ExtraAllocSize(allocator_)) - .ValueOrDie(), - type_name))) {} - - ~ScopedPageAllocation() { allocator_.root()->Free(ptr_); } - - void TouchAllPages() { - memset(ptr_, 'A', - ((npages_ * SystemPageSize()) - ExtraAllocSize(allocator_)) - .ValueOrDie()); - } - - void* PageAtIndex(size_t index) { - return ptr_ - kPointerOffset + (SystemPageSize() * index); - } - - private: - PartitionAllocator<internal::ThreadSafe>& allocator_; - const base::CheckedNumeric<size_t> npages_; - char* ptr_; -}; - -struct PartitionAllocTestParam { - PartitionAllocTestParam(BucketDistribution bucket_distribution, - bool use_pkey_pool) - : bucket_distribution(bucket_distribution), - use_pkey_pool(use_pkey_pool) {} - BucketDistribution bucket_distribution; - bool use_pkey_pool; -}; - -const std::vector<PartitionAllocTestParam> GetPartitionAllocTestParams() { - std::vector<PartitionAllocTestParam> params; - params.emplace_back(BucketDistribution::kDefault, false); - params.emplace_back(BucketDistribution::kDenser, false); -#if BUILDFLAG(ENABLE_PKEYS) - if (CPUHasPkeySupport()) { - params.emplace_back(BucketDistribution::kDefault, true); - params.emplace_back(BucketDistribution::kDenser, true); - } -#endif - return params; -} - -class PartitionAllocTest - : public testing::TestWithParam<PartitionAllocTestParam> { - protected: - PartitionAllocTest() = default; - - ~PartitionAllocTest() override = default; - - void InitializeAllocator() { -#if BUILDFLAG(ENABLE_PKEYS) - int pkey = PkeyAlloc(UsePkeyPool() ? 0 : PKEY_DISABLE_WRITE); - if (pkey != -1) { - pkey_ = pkey; - } - // We always want to have a pkey allocator initialized to make sure that the - // other pools still work. As part of the initializition, we tag some memory - // with the new pkey, effectively making it read-only. So there's some - // potential for breakage that this should catch. - pkey_allocator.init({ - partition_alloc::PartitionOptions::AlignedAlloc::kAllowed, - partition_alloc::PartitionOptions::ThreadCache::kDisabled, - partition_alloc::PartitionOptions::Quarantine::kDisallowed, - partition_alloc::PartitionOptions::Cookie::kAllowed, - partition_alloc::PartitionOptions::BackupRefPtr::kDisabled, - partition_alloc::PartitionOptions::BackupRefPtrZapping::kDisabled, - partition_alloc::PartitionOptions::UseConfigurablePool::kNo, - partition_alloc::PartitionOptions::AddDummyRefCount::kDisabled, - pkey_ != kInvalidPkey ? pkey_ : kDefaultPkey, - }); - if (UsePkeyPool() && pkey_ != kInvalidPkey) { - allocator.init({ - partition_alloc::PartitionOptions::AlignedAlloc::kAllowed, - partition_alloc::PartitionOptions::ThreadCache::kDisabled, - partition_alloc::PartitionOptions::Quarantine::kDisallowed, - partition_alloc::PartitionOptions::Cookie::kAllowed, - partition_alloc::PartitionOptions::BackupRefPtr::kDisabled, - partition_alloc::PartitionOptions::BackupRefPtrZapping::kDisabled, - partition_alloc::PartitionOptions::UseConfigurablePool::kNo, - partition_alloc::PartitionOptions::AddDummyRefCount::kDisabled, - pkey_, - }); - return; - } -#endif - allocator.init({ -#if !BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) || \ - BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) - // AlignedAllocWithFlags() can't be called when BRP is in the "before - // allocation" mode, because this mode adds extras before the allocation. - // Extras after the allocation are ok. - PartitionOptions::AlignedAlloc::kAllowed, -#else - PartitionOptions::AlignedAlloc::kDisallowed, -#endif - PartitionOptions::ThreadCache::kDisabled, - PartitionOptions::Quarantine::kDisallowed, - PartitionOptions::Cookie::kAllowed, -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - PartitionOptions::BackupRefPtr::kEnabled, - PartitionOptions::BackupRefPtrZapping::kEnabled, -#else - PartitionOptions::BackupRefPtr::kDisabled, - PartitionOptions::BackupRefPtrZapping::kDisabled, -#endif - PartitionOptions::UseConfigurablePool::kNo, - }); - } - - size_t RealAllocSize() const { - return partition_alloc::internal::base::bits::AlignUp( - kTestAllocSize + ExtraAllocSize(allocator), kAlignment); - } - - void SetUp() override { - PartitionRoot<ThreadSafe>::EnableSortActiveSlotSpans(); - PartitionAllocGlobalInit(HandleOOM); - InitializeAllocator(); - - aligned_allocator.init({ - PartitionOptions::AlignedAlloc::kAllowed, - PartitionOptions::ThreadCache::kDisabled, - PartitionOptions::Quarantine::kDisallowed, - PartitionOptions::Cookie::kDisallowed, - PartitionOptions::BackupRefPtr::kDisabled, - PartitionOptions::BackupRefPtrZapping::kDisabled, - PartitionOptions::UseConfigurablePool::kNo, - }); - test_bucket_index_ = SizeToIndex(RealAllocSize()); - allocator.root()->UncapEmptySlotSpanMemoryForTesting(); - aligned_allocator.root()->UncapEmptySlotSpanMemoryForTesting(); - - SetDistributionForPartitionRoot(allocator.root(), GetBucketDistribution()); - SetDistributionForPartitionRoot(aligned_allocator.root(), - GetBucketDistribution()); - } - - size_t SizeToIndex(size_t size) { - const auto distribution_to_use = GetBucketDistribution(); - return PartitionRoot<internal::ThreadSafe>::SizeToBucketIndex( - size, distribution_to_use); - } - - size_t SizeToBucketSize(size_t size) { - const auto index = SizeToIndex(size); - return allocator.root()->buckets[index].slot_size; - } - - void TearDown() override { - allocator.root()->PurgeMemory(PurgeFlags::kDecommitEmptySlotSpans | - PurgeFlags::kDiscardUnusedSystemPages); - PartitionAllocGlobalUninitForTesting(); -#if BUILDFLAG(ENABLE_PKEYS) - if (pkey_ != kInvalidPkey) { - PkeyFree(pkey_); - } -#endif - } - - size_t GetNumPagesPerSlotSpan(size_t size) { - size_t real_size = size + ExtraAllocSize(allocator); - size_t bucket_index = SizeToIndex(real_size); - PartitionRoot<ThreadSafe>::Bucket* bucket = - &allocator.root()->buckets[bucket_index]; - // TODO(tasak): make get_pages_per_slot_span() available at - // partition_alloc_unittest.cc. Is it allowable to make the code from - // partition_bucet.cc to partition_bucket.h? - return (bucket->num_system_pages_per_slot_span + - (NumSystemPagesPerPartitionPage() - 1)) / - NumSystemPagesPerPartitionPage(); - } - - SlotSpan* GetFullSlotSpan(size_t size) { - size_t real_size = size + ExtraAllocSize(allocator); - size_t bucket_index = SizeToIndex(real_size); - PartitionRoot<ThreadSafe>::Bucket* bucket = - &allocator.root()->buckets[bucket_index]; - size_t num_slots = - (bucket->num_system_pages_per_slot_span * SystemPageSize()) / - bucket->slot_size; - uintptr_t first = 0; - uintptr_t last = 0; - size_t i; - for (i = 0; i < num_slots; ++i) { - void* ptr = allocator.root()->Alloc(size, type_name); - EXPECT_TRUE(ptr); - if (!i) { - first = allocator.root()->ObjectToSlotStart(ptr); - } else if (i == num_slots - 1) { - last = allocator.root()->ObjectToSlotStart(ptr); - } - } - EXPECT_EQ(SlotSpan::FromSlotStart(first), SlotSpan::FromSlotStart(last)); - if (bucket->num_system_pages_per_slot_span == - NumSystemPagesPerPartitionPage()) { - EXPECT_EQ(first & PartitionPageBaseMask(), - last & PartitionPageBaseMask()); - } - EXPECT_EQ(num_slots, bucket->active_slot_spans_head->num_allocated_slots); - EXPECT_EQ(nullptr, bucket->active_slot_spans_head->get_freelist_head()); - EXPECT_TRUE(bucket->is_valid()); - EXPECT_TRUE(bucket->active_slot_spans_head != - SlotSpan::get_sentinel_slot_span()); - EXPECT_TRUE(bucket->active_slot_spans_head->is_full()); - return bucket->active_slot_spans_head; - } - - void CycleFreeCache(size_t size) { - for (size_t i = 0; i < kMaxFreeableSpans; ++i) { - void* ptr = allocator.root()->Alloc(size, type_name); - auto* slot_span = - SlotSpan::FromSlotStart(allocator.root()->ObjectToSlotStart(ptr)); - auto* bucket = slot_span->bucket; - EXPECT_EQ(1u, bucket->active_slot_spans_head->num_allocated_slots); - allocator.root()->Free(ptr); - EXPECT_EQ(0u, bucket->active_slot_spans_head->num_allocated_slots); - EXPECT_TRUE(bucket->active_slot_spans_head->in_empty_cache() || - bucket->active_slot_spans_head == - SlotSpanMetadata<ThreadSafe>::get_sentinel_slot_span()); - } - } - - enum ReturnNullTestMode { - kPartitionAllocWithFlags, - kPartitionReallocWithFlags, - kPartitionRootTryRealloc, - }; - - void DoReturnNullTest(size_t alloc_size, ReturnNullTestMode mode) { - // TODO(crbug.com/678782): Where necessary and possible, disable the - // platform's OOM-killing behavior. OOM-killing makes this test flaky on - // low-memory devices. - if (!IsLargeMemoryDevice()) { - PA_LOG(WARNING) - << "Skipping test on this device because of crbug.com/678782"; - PA_LOG(FATAL) << "Passed DoReturnNullTest"; - } - - ASSERT_TRUE(SetAddressSpaceLimit()); - - // Work out the number of allocations for 6 GB of memory. - const int num_allocations = (6 * 1024 * 1024) / (alloc_size / 1024); - - void** ptrs = static_cast<void**>( - allocator.root()->Alloc(num_allocations * sizeof(void*), type_name)); - int i; - - for (i = 0; i < num_allocations; ++i) { - switch (mode) { - case kPartitionAllocWithFlags: { - ptrs[i] = allocator.root()->AllocWithFlags(AllocFlags::kReturnNull, - alloc_size, type_name); - break; - } - case kPartitionReallocWithFlags: { - ptrs[i] = allocator.root()->AllocWithFlags(AllocFlags::kReturnNull, 1, - type_name); - ptrs[i] = allocator.root()->ReallocWithFlags( - AllocFlags::kReturnNull, ptrs[i], alloc_size, type_name); - break; - } - case kPartitionRootTryRealloc: { - ptrs[i] = allocator.root()->AllocWithFlags(AllocFlags::kReturnNull, 1, - type_name); - ptrs[i] = - allocator.root()->TryRealloc(ptrs[i], alloc_size, type_name); - } - } - - if (!i) { - EXPECT_TRUE(ptrs[0]); - } - if (!ptrs[i]) { - ptrs[i] = allocator.root()->AllocWithFlags(AllocFlags::kReturnNull, - alloc_size, type_name); - EXPECT_FALSE(ptrs[i]); - break; - } - } - - // We shouldn't succeed in allocating all 6 GB of memory. If we do, then - // we're not actually testing anything here. - EXPECT_LT(i, num_allocations); - - // Free, reallocate and free again each block we allocated. We do this to - // check that freeing memory also works correctly after a failed allocation. - for (--i; i >= 0; --i) { - allocator.root()->Free(ptrs[i]); - ptrs[i] = allocator.root()->AllocWithFlags(AllocFlags::kReturnNull, - alloc_size, type_name); - EXPECT_TRUE(ptrs[i]); - allocator.root()->Free(ptrs[i]); - } - - allocator.root()->Free(ptrs); - - EXPECT_TRUE(ClearAddressSpaceLimit()); - PA_LOG(FATAL) << "Passed DoReturnNullTest"; - } - - void RunRefCountReallocSubtest(size_t orig_size, size_t new_size); - - PA_NOINLINE PA_MALLOC_FN void* Alloc(size_t size) { - return allocator.root()->Alloc(size, ""); - } - - PA_NOINLINE void Free(void* ptr) { allocator.root()->Free(ptr); } - - BucketDistribution GetBucketDistribution() const { - return GetParam().bucket_distribution; - } - - bool UsePkeyPool() const { return GetParam().use_pkey_pool; } - bool UseBRPPool() const { return allocator.root()->brp_enabled(); } - - partition_alloc::PartitionAllocatorForTesting allocator; - partition_alloc::PartitionAllocatorForTesting aligned_allocator; -#if BUILDFLAG(ENABLE_PKEYS) - partition_alloc::PartitionAllocatorForTesting pkey_allocator; -#endif - size_t test_bucket_index_; - -#if BUILDFLAG(ENABLE_PKEYS) - int pkey_ = kInvalidPkey; -#endif -}; - -// Death tests misbehave on Android, http://crbug.com/643760. -#if defined(GTEST_HAS_DEATH_TEST) && !BUILDFLAG(IS_ANDROID) -#define PA_HAS_DEATH_TESTS - -class PartitionAllocDeathTest : public PartitionAllocTest {}; - -INSTANTIATE_TEST_SUITE_P(AlternateBucketDistribution, - PartitionAllocDeathTest, - testing::ValuesIn(GetPartitionAllocTestParams())); - -#endif - -namespace { - -void FreeFullSlotSpan(PartitionRoot<internal::ThreadSafe>* root, - SlotSpan* slot_span) { - EXPECT_TRUE(slot_span->is_full()); - size_t size = slot_span->bucket->slot_size; - size_t num_slots = - (slot_span->bucket->num_system_pages_per_slot_span * SystemPageSize()) / - size; - EXPECT_EQ(num_slots, slot_span->num_allocated_slots); - uintptr_t address = SlotSpan::ToSlotSpanStart(slot_span); - size_t i; - for (i = 0; i < num_slots; ++i) { - root->Free(root->SlotStartToObject(address)); - address += size; - } - EXPECT_TRUE(slot_span->is_empty()); -} - -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) -bool CheckPageInCore(void* ptr, bool in_core) { - unsigned char ret = 0; - EXPECT_EQ(0, mincore(ptr, SystemPageSize(), &ret)); - return in_core == (ret & 1); -} - -#define CHECK_PAGE_IN_CORE(ptr, in_core) \ - EXPECT_TRUE(CheckPageInCore(ptr, in_core)) -#else -#define CHECK_PAGE_IN_CORE(ptr, in_core) (void)(0) -#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) - -class MockPartitionStatsDumper : public PartitionStatsDumper { - public: - MockPartitionStatsDumper() = default; - - void PartitionDumpTotals(const char* partition_name, - const PartitionMemoryStats* stats) override { - EXPECT_GE(stats->total_mmapped_bytes, stats->total_resident_bytes); - EXPECT_EQ(total_resident_bytes, stats->total_resident_bytes); - EXPECT_EQ(total_active_bytes, stats->total_active_bytes); - EXPECT_EQ(total_decommittable_bytes, stats->total_decommittable_bytes); - EXPECT_EQ(total_discardable_bytes, stats->total_discardable_bytes); - } - - void PartitionsDumpBucketStats( - [[maybe_unused]] const char* partition_name, - const PartitionBucketMemoryStats* stats) override { - EXPECT_TRUE(stats->is_valid); - EXPECT_EQ(0u, stats->bucket_slot_size & sizeof(void*)); - bucket_stats.push_back(*stats); - total_resident_bytes += stats->resident_bytes; - total_active_bytes += stats->active_bytes; - total_decommittable_bytes += stats->decommittable_bytes; - total_discardable_bytes += stats->discardable_bytes; - } - - bool IsMemoryAllocationRecorded() { - return total_resident_bytes != 0 && total_active_bytes != 0; - } - - const PartitionBucketMemoryStats* GetBucketStats(size_t bucket_size) { - for (auto& stat : bucket_stats) { - if (stat.bucket_slot_size == bucket_size) { - return &stat; - } - } - return nullptr; - } - - private: - size_t total_resident_bytes = 0; - size_t total_active_bytes = 0; - size_t total_decommittable_bytes = 0; - size_t total_discardable_bytes = 0; - - std::vector<PartitionBucketMemoryStats> bucket_stats; -}; - -} // namespace - -INSTANTIATE_TEST_SUITE_P(AlternateBucketDistribution, - PartitionAllocTest, - testing::ValuesIn(GetPartitionAllocTestParams())); - -// Check that the most basic of allocate / free pairs work. -TEST_P(PartitionAllocTest, Basic) { - PartitionRoot<ThreadSafe>::Bucket* bucket = - &allocator.root()->buckets[test_bucket_index_]; - auto* seed_slot_span = SlotSpan::get_sentinel_slot_span(); - - EXPECT_FALSE(bucket->empty_slot_spans_head); - EXPECT_FALSE(bucket->decommitted_slot_spans_head); - EXPECT_EQ(seed_slot_span, bucket->active_slot_spans_head); - EXPECT_EQ(nullptr, bucket->active_slot_spans_head->next_slot_span); - - void* ptr = allocator.root()->Alloc(kTestAllocSize, type_name); - EXPECT_TRUE(ptr); - EXPECT_EQ(kPointerOffset, UntagPtr(ptr) & PartitionPageOffsetMask()); - // Check that the offset appears to include a guard page. - EXPECT_EQ(PartitionPageSize() + - partition_alloc::internal::ReservedFreeSlotBitmapSize() + - kPointerOffset, - UntagPtr(ptr) & kSuperPageOffsetMask); - - allocator.root()->Free(ptr); - // Expect that the last active slot span gets noticed as empty but doesn't get - // decommitted. - EXPECT_TRUE(bucket->empty_slot_spans_head); - EXPECT_FALSE(bucket->decommitted_slot_spans_head); -} - -// Test multiple allocations, and freelist handling. -TEST_P(PartitionAllocTest, MultiAlloc) { - void* ptr1 = allocator.root()->Alloc(kTestAllocSize, type_name); - void* ptr2 = allocator.root()->Alloc(kTestAllocSize, type_name); - EXPECT_TRUE(ptr1); - EXPECT_TRUE(ptr2); - ptrdiff_t diff = UntagPtr(ptr2) - UntagPtr(ptr1); - EXPECT_EQ(static_cast<ptrdiff_t>(RealAllocSize()), diff); - - // Check that we re-use the just-freed slot. - allocator.root()->Free(ptr2); - ptr2 = allocator.root()->Alloc(kTestAllocSize, type_name); - EXPECT_TRUE(ptr2); - diff = UntagPtr(ptr2) - UntagPtr(ptr1); - EXPECT_EQ(static_cast<ptrdiff_t>(RealAllocSize()), diff); - allocator.root()->Free(ptr1); - ptr1 = allocator.root()->Alloc(kTestAllocSize, type_name); - EXPECT_TRUE(ptr1); - diff = UntagPtr(ptr2) - UntagPtr(ptr1); - EXPECT_EQ(static_cast<ptrdiff_t>(RealAllocSize()), diff); - - void* ptr3 = allocator.root()->Alloc(kTestAllocSize, type_name); - EXPECT_TRUE(ptr3); - diff = UntagPtr(ptr3) - UntagPtr(ptr1); - EXPECT_EQ(static_cast<ptrdiff_t>(RealAllocSize() * 2), diff); - - allocator.root()->Free(ptr1); - allocator.root()->Free(ptr2); - allocator.root()->Free(ptr3); -} - -// Test a bucket with multiple slot spans. -TEST_P(PartitionAllocTest, MultiSlotSpans) { - PartitionRoot<ThreadSafe>::Bucket* bucket = - &allocator.root()->buckets[test_bucket_index_]; - - auto* slot_span = GetFullSlotSpan(kTestAllocSize); - FreeFullSlotSpan(allocator.root(), slot_span); - EXPECT_TRUE(bucket->empty_slot_spans_head); - EXPECT_EQ(SlotSpan::get_sentinel_slot_span(), bucket->active_slot_spans_head); - EXPECT_EQ(nullptr, slot_span->next_slot_span); - EXPECT_EQ(0u, slot_span->num_allocated_slots); - - slot_span = GetFullSlotSpan(kTestAllocSize); - auto* slot_span2 = GetFullSlotSpan(kTestAllocSize); - - EXPECT_EQ(slot_span2, bucket->active_slot_spans_head); - EXPECT_EQ(nullptr, slot_span2->next_slot_span); - EXPECT_EQ(SlotSpan::ToSlotSpanStart(slot_span) & kSuperPageBaseMask, - SlotSpan::ToSlotSpanStart(slot_span2) & kSuperPageBaseMask); - - // Fully free the non-current slot span. This will leave us with no current - // active slot span because one is empty and the other is full. - FreeFullSlotSpan(allocator.root(), slot_span); - EXPECT_EQ(0u, slot_span->num_allocated_slots); - EXPECT_TRUE(bucket->empty_slot_spans_head); - EXPECT_EQ(SlotSpanMetadata<ThreadSafe>::get_sentinel_slot_span(), - bucket->active_slot_spans_head); - - // Allocate a new slot span, it should pull from the freelist. - slot_span = GetFullSlotSpan(kTestAllocSize); - EXPECT_FALSE(bucket->empty_slot_spans_head); - EXPECT_EQ(slot_span, bucket->active_slot_spans_head); - - FreeFullSlotSpan(allocator.root(), slot_span); - FreeFullSlotSpan(allocator.root(), slot_span2); - EXPECT_EQ(0u, slot_span->num_allocated_slots); - EXPECT_EQ(0u, slot_span2->num_allocated_slots); - EXPECT_EQ(0u, slot_span2->num_unprovisioned_slots); - EXPECT_TRUE(slot_span2->in_empty_cache()); -} - -// Test some finer aspects of internal slot span transitions. -TEST_P(PartitionAllocTest, SlotSpanTransitions) { - PartitionRoot<ThreadSafe>::Bucket* bucket = - &allocator.root()->buckets[test_bucket_index_]; - - auto* slot_span1 = GetFullSlotSpan(kTestAllocSize); - EXPECT_EQ(slot_span1, bucket->active_slot_spans_head); - EXPECT_EQ(nullptr, slot_span1->next_slot_span); - auto* slot_span2 = GetFullSlotSpan(kTestAllocSize); - EXPECT_EQ(slot_span2, bucket->active_slot_spans_head); - EXPECT_EQ(nullptr, slot_span2->next_slot_span); - - // Bounce slot_span1 back into the non-full list then fill it up again. - void* ptr = allocator.root()->SlotStartToObject( - SlotSpan::ToSlotSpanStart(slot_span1)); - allocator.root()->Free(ptr); - EXPECT_EQ(slot_span1, bucket->active_slot_spans_head); - std::ignore = allocator.root()->Alloc(kTestAllocSize, type_name); - EXPECT_EQ(slot_span1, bucket->active_slot_spans_head); - EXPECT_EQ(slot_span2, bucket->active_slot_spans_head->next_slot_span); - - // Allocating another slot span at this point should cause us to scan over - // slot_span1 (which is both full and NOT our current slot span), and evict it - // from the freelist. Older code had a O(n^2) condition due to failure to do - // this. - auto* slot_span3 = GetFullSlotSpan(kTestAllocSize); - EXPECT_EQ(slot_span3, bucket->active_slot_spans_head); - EXPECT_EQ(nullptr, slot_span3->next_slot_span); - - // Work out a pointer into slot_span2 and free it. - ptr = allocator.root()->SlotStartToObject( - SlotSpan::ToSlotSpanStart(slot_span2)); - allocator.root()->Free(ptr); - // Trying to allocate at this time should cause us to cycle around to - // slot_span2 and find the recently freed slot. - void* ptr2 = allocator.root()->Alloc(kTestAllocSize, type_name); - PA_EXPECT_PTR_EQ(ptr, ptr2); - EXPECT_EQ(slot_span2, bucket->active_slot_spans_head); - EXPECT_EQ(slot_span3, slot_span2->next_slot_span); - - // Work out a pointer into slot_span1 and free it. This should pull the slot - // span back into the list of available slot spans. - ptr = allocator.root()->SlotStartToObject( - SlotSpan::ToSlotSpanStart(slot_span1)); - allocator.root()->Free(ptr); - // This allocation should be satisfied by slot_span1. - ptr2 = allocator.root()->Alloc(kTestAllocSize, type_name); - PA_EXPECT_PTR_EQ(ptr, ptr2); - EXPECT_EQ(slot_span1, bucket->active_slot_spans_head); - EXPECT_EQ(slot_span2, slot_span1->next_slot_span); - - FreeFullSlotSpan(allocator.root(), slot_span3); - FreeFullSlotSpan(allocator.root(), slot_span2); - FreeFullSlotSpan(allocator.root(), slot_span1); - - // Allocating whilst in this state exposed a bug, so keep the test. - ptr = allocator.root()->Alloc(kTestAllocSize, type_name); - allocator.root()->Free(ptr); -} - -TEST_P(PartitionAllocTest, PreferSlotSpansWithProvisionedEntries) { - size_t size = SystemPageSize() - ExtraAllocSize(allocator); - size_t real_size = size + ExtraAllocSize(allocator); - size_t bucket_index = - allocator.root()->SizeToBucketIndex(real_size, GetBucketDistribution()); - PartitionRoot<ThreadSafe>::Bucket* bucket = - &allocator.root()->buckets[bucket_index]; - ASSERT_EQ(bucket->slot_size, real_size); - size_t slots_per_span = bucket->num_system_pages_per_slot_span; - - // Make 10 full slot spans. - constexpr int kSpans = 10; - std::vector<std::vector<void*>> allocated_memory_spans(kSpans); - for (int span_index = 0; span_index < kSpans; span_index++) { - for (size_t i = 0; i < slots_per_span; i++) { - allocated_memory_spans[span_index].push_back( - allocator.root()->Alloc(size, "")); - } - } - - // Reverse ordering, since a newly non-full span is placed at the head of the - // active list. - for (int span_index = kSpans - 1; span_index >= 0; span_index--) { - allocator.root()->Free(allocated_memory_spans[span_index].back()); - allocated_memory_spans[span_index].pop_back(); - } - - // Since slot spans are large enough and we freed memory from the end, the - // slot spans become partially provisioned after PurgeMemory(). - allocator.root()->PurgeMemory(PurgeFlags::kDecommitEmptySlotSpans | - PurgeFlags::kDiscardUnusedSystemPages); - std::vector<SlotSpanMetadata<ThreadSafe>*> active_slot_spans; - for (auto* span = bucket->active_slot_spans_head; span; - span = span->next_slot_span) { - active_slot_spans.push_back(span); - ASSERT_EQ(span->num_unprovisioned_slots, 1u); - // But no freelist entries. - ASSERT_FALSE(span->get_freelist_head()); - } - - // Free one entry in the middle span, creating a freelist entry. - constexpr size_t kSpanIndex = 5; - allocator.root()->Free(allocated_memory_spans[kSpanIndex].back()); - allocated_memory_spans[kSpanIndex].pop_back(); - - ASSERT_TRUE(active_slot_spans[kSpanIndex]->get_freelist_head()); - ASSERT_FALSE(bucket->active_slot_spans_head->get_freelist_head()); - - // It must come from the middle slot span even though the first one has - // unprovisioned space. - void* new_ptr = allocator.root()->Alloc(size, ""); - - // Comes from the middle slot span, since it has a freelist entry. - auto* new_active_slot_span = active_slot_spans[kSpanIndex]; - ASSERT_FALSE(new_active_slot_span->get_freelist_head()); - - // The middle slot span was moved to the front. - active_slot_spans.erase(active_slot_spans.begin() + kSpanIndex); - active_slot_spans.insert(active_slot_spans.begin(), new_active_slot_span); - - // Check slot span ordering. - int index = 0; - for (auto* span = bucket->active_slot_spans_head; span; - span = span->next_slot_span) { - EXPECT_EQ(span, active_slot_spans[index]); - index++; - } - EXPECT_EQ(index, kSpans); - - allocator.root()->Free(new_ptr); - for (int span_index = 0; span_index < kSpans; span_index++) { - for (void* ptr : allocated_memory_spans[span_index]) { - allocator.root()->Free(ptr); - } - } -} - -// Test some corner cases relating to slot span transitions in the internal -// free slot span list metadata bucket. -TEST_P(PartitionAllocTest, FreeSlotSpanListSlotSpanTransitions) { - PartitionRoot<ThreadSafe>::Bucket* bucket = - &allocator.root()->buckets[test_bucket_index_]; - - size_t num_to_fill_free_list_slot_span = - PartitionPageSize() / (sizeof(SlotSpan) + ExtraAllocSize(allocator)); - // The +1 is because we need to account for the fact that the current slot - // span never gets thrown on the freelist. - ++num_to_fill_free_list_slot_span; - auto slot_spans = - std::make_unique<SlotSpan*[]>(num_to_fill_free_list_slot_span); - - size_t i; - for (i = 0; i < num_to_fill_free_list_slot_span; ++i) { - slot_spans[i] = GetFullSlotSpan(kTestAllocSize); - } - EXPECT_EQ(slot_spans[num_to_fill_free_list_slot_span - 1], - bucket->active_slot_spans_head); - for (i = 0; i < num_to_fill_free_list_slot_span; ++i) { - FreeFullSlotSpan(allocator.root(), slot_spans[i]); - } - EXPECT_EQ(SlotSpan::get_sentinel_slot_span(), bucket->active_slot_spans_head); - EXPECT_TRUE(bucket->empty_slot_spans_head); - - // Allocate / free in a different bucket size so we get control of a - // different free slot span list. We need two slot spans because one will be - // the last active slot span and not get freed. - auto* slot_span1 = GetFullSlotSpan(kTestAllocSize * 2); - auto* slot_span2 = GetFullSlotSpan(kTestAllocSize * 2); - FreeFullSlotSpan(allocator.root(), slot_span1); - FreeFullSlotSpan(allocator.root(), slot_span2); - - for (i = 0; i < num_to_fill_free_list_slot_span; ++i) { - slot_spans[i] = GetFullSlotSpan(kTestAllocSize); - } - EXPECT_EQ(slot_spans[num_to_fill_free_list_slot_span - 1], - bucket->active_slot_spans_head); - - for (i = 0; i < num_to_fill_free_list_slot_span; ++i) { - FreeFullSlotSpan(allocator.root(), slot_spans[i]); - } - EXPECT_EQ(SlotSpan::get_sentinel_slot_span(), bucket->active_slot_spans_head); - EXPECT_TRUE(bucket->empty_slot_spans_head); -} - -// Test a large series of allocations that cross more than one underlying -// super page. -TEST_P(PartitionAllocTest, MultiPageAllocs) { - size_t num_pages_per_slot_span = GetNumPagesPerSlotSpan(kTestAllocSize); - // 1 super page has 2 guard partition pages and a tag bitmap. - size_t num_slot_spans_needed = - (NumPartitionPagesPerSuperPage() - 2 - - partition_alloc::internal::NumPartitionPagesPerFreeSlotBitmap()) / - num_pages_per_slot_span; - - // We need one more slot span in order to cross super page boundary. - ++num_slot_spans_needed; - - EXPECT_GT(num_slot_spans_needed, 1u); - auto slot_spans = std::make_unique<SlotSpan*[]>(num_slot_spans_needed); - uintptr_t first_super_page_base = 0; - size_t i; - for (i = 0; i < num_slot_spans_needed; ++i) { - slot_spans[i] = GetFullSlotSpan(kTestAllocSize); - uintptr_t slot_span_start = SlotSpan::ToSlotSpanStart(slot_spans[i]); - if (!i) { - first_super_page_base = slot_span_start & kSuperPageBaseMask; - } - if (i == num_slot_spans_needed - 1) { - uintptr_t second_super_page_base = slot_span_start & kSuperPageBaseMask; - uintptr_t second_super_page_offset = - slot_span_start & kSuperPageOffsetMask; - EXPECT_FALSE(second_super_page_base == first_super_page_base); - // Check that we allocated a guard page and the reserved tag bitmap for - // the second page. - EXPECT_EQ(PartitionPageSize() + - partition_alloc::internal::ReservedFreeSlotBitmapSize(), - second_super_page_offset); - } - } - for (i = 0; i < num_slot_spans_needed; ++i) { - FreeFullSlotSpan(allocator.root(), slot_spans[i]); - } -} - -// Test the generic allocation functions that can handle arbitrary sizes and -// reallocing etc. -TEST_P(PartitionAllocTest, Alloc) { - void* ptr = allocator.root()->Alloc(1, type_name); - EXPECT_TRUE(ptr); - allocator.root()->Free(ptr); - ptr = allocator.root()->Alloc(kMaxBucketed + 1, type_name); - EXPECT_TRUE(ptr); - allocator.root()->Free(ptr); - - // To make both alloc(x + 1) and alloc(x + kSmallestBucket) to allocate from - // the same bucket, partition_alloc::internal::base::bits::AlignUp(1 + x + - // ExtraAllocSize(allocator), kAlignment) - // == partition_alloc::internal::base::bits::AlignUp(kSmallestBucket + x + - // ExtraAllocSize(allocator), kAlignment), because slot_size is multiples of - // kAlignment. So (x + ExtraAllocSize(allocator)) must be multiples of - // kAlignment. x = - // partition_alloc::internal::base::bits::AlignUp(ExtraAllocSize(allocator), - // kAlignment) - ExtraAllocSize(allocator); - size_t base_size = partition_alloc::internal::base::bits::AlignUp( - ExtraAllocSize(allocator), kAlignment) - - ExtraAllocSize(allocator); - ptr = allocator.root()->Alloc(base_size + 1, type_name); - EXPECT_TRUE(ptr); - void* orig_ptr = ptr; - char* char_ptr = static_cast<char*>(ptr); - *char_ptr = 'A'; - - // Change the size of the realloc, remaining inside the same bucket. - void* new_ptr = allocator.root()->Realloc(ptr, base_size + 2, type_name); - PA_EXPECT_PTR_EQ(ptr, new_ptr); - new_ptr = allocator.root()->Realloc(ptr, base_size + 1, type_name); - PA_EXPECT_PTR_EQ(ptr, new_ptr); - new_ptr = - allocator.root()->Realloc(ptr, base_size + kSmallestBucket, type_name); - PA_EXPECT_PTR_EQ(ptr, new_ptr); - - // Change the size of the realloc, switching buckets. - new_ptr = allocator.root()->Realloc(ptr, base_size + kSmallestBucket + 1, - type_name); - PA_EXPECT_PTR_NE(new_ptr, ptr); - // Check that the realloc copied correctly. - char* new_char_ptr = static_cast<char*>(new_ptr); - EXPECT_EQ(*new_char_ptr, 'A'); -#if BUILDFLAG(PA_EXPENSIVE_DCHECKS_ARE_ON) - // Subtle: this checks for an old bug where we copied too much from the - // source of the realloc. The condition can be detected by a trashing of - // the uninitialized value in the space of the upsized allocation. - EXPECT_EQ(kUninitializedByte, - static_cast<unsigned char>(*(new_char_ptr + kSmallestBucket))); -#endif - *new_char_ptr = 'B'; - // The realloc moved. To check that the old allocation was freed, we can - // do an alloc of the old allocation size and check that the old allocation - // address is at the head of the freelist and reused. - void* reused_ptr = allocator.root()->Alloc(base_size + 1, type_name); - PA_EXPECT_PTR_EQ(reused_ptr, orig_ptr); - allocator.root()->Free(reused_ptr); - - // Downsize the realloc. - ptr = new_ptr; - new_ptr = allocator.root()->Realloc(ptr, base_size + 1, type_name); - PA_EXPECT_PTR_EQ(new_ptr, orig_ptr); - new_char_ptr = static_cast<char*>(new_ptr); - EXPECT_EQ(*new_char_ptr, 'B'); - *new_char_ptr = 'C'; - - // Upsize the realloc to outside the partition. - ptr = new_ptr; - new_ptr = allocator.root()->Realloc(ptr, kMaxBucketed + 1, type_name); - PA_EXPECT_PTR_NE(new_ptr, ptr); - new_char_ptr = static_cast<char*>(new_ptr); - EXPECT_EQ(*new_char_ptr, 'C'); - *new_char_ptr = 'D'; - - // Upsize and downsize the realloc, remaining outside the partition. - ptr = new_ptr; - new_ptr = allocator.root()->Realloc(ptr, kMaxBucketed * 10, type_name); - new_char_ptr = static_cast<char*>(new_ptr); - EXPECT_EQ(*new_char_ptr, 'D'); - *new_char_ptr = 'E'; - ptr = new_ptr; - new_ptr = allocator.root()->Realloc(ptr, kMaxBucketed * 2, type_name); - new_char_ptr = static_cast<char*>(new_ptr); - EXPECT_EQ(*new_char_ptr, 'E'); - *new_char_ptr = 'F'; - - // Downsize the realloc to inside the partition. - ptr = new_ptr; - new_ptr = allocator.root()->Realloc(ptr, base_size + 1, type_name); - PA_EXPECT_PTR_NE(new_ptr, ptr); - PA_EXPECT_PTR_EQ(new_ptr, orig_ptr); - new_char_ptr = static_cast<char*>(new_ptr); - EXPECT_EQ(*new_char_ptr, 'F'); - - allocator.root()->Free(new_ptr); -} - -// Test the generic allocation functions can handle some specific sizes of -// interest. -TEST_P(PartitionAllocTest, AllocSizes) { - { - void* ptr = allocator.root()->Alloc(0, type_name); - EXPECT_TRUE(ptr); - allocator.root()->Free(ptr); - } - - { - // PartitionPageSize() is interesting because it results in just one - // allocation per page, which tripped up some corner cases. - const size_t size = PartitionPageSize() - ExtraAllocSize(allocator); - void* ptr = allocator.root()->Alloc(size, type_name); - EXPECT_TRUE(ptr); - void* ptr2 = allocator.root()->Alloc(size, type_name); - EXPECT_TRUE(ptr2); - allocator.root()->Free(ptr); - // Should be freeable at this point. - auto* slot_span = - SlotSpan::FromSlotStart(allocator.root()->ObjectToSlotStart(ptr)); - EXPECT_TRUE(slot_span->in_empty_cache()); - allocator.root()->Free(ptr2); - } - - { - // Single-slot slot span size. - const size_t size = - PartitionPageSize() * kMaxPartitionPagesPerRegularSlotSpan + 1; - - void* ptr = allocator.root()->Alloc(size, type_name); - EXPECT_TRUE(ptr); - memset(ptr, 'A', size); - void* ptr2 = allocator.root()->Alloc(size, type_name); - EXPECT_TRUE(ptr2); - void* ptr3 = allocator.root()->Alloc(size, type_name); - EXPECT_TRUE(ptr3); - void* ptr4 = allocator.root()->Alloc(size, type_name); - EXPECT_TRUE(ptr4); - - auto* slot_span = SlotSpanMetadata<internal::ThreadSafe>::FromSlotStart( - allocator.root()->ObjectToSlotStart(ptr)); - auto* slot_span2 = - SlotSpan::FromSlotStart(allocator.root()->ObjectToSlotStart(ptr3)); - EXPECT_NE(slot_span, slot_span2); - - allocator.root()->Free(ptr); - allocator.root()->Free(ptr3); - allocator.root()->Free(ptr2); - // Should be freeable at this point. - EXPECT_TRUE(slot_span->in_empty_cache()); - EXPECT_EQ(0u, slot_span->num_allocated_slots); - EXPECT_EQ(0u, slot_span->num_unprovisioned_slots); - void* new_ptr_1 = allocator.root()->Alloc(size, type_name); - PA_EXPECT_PTR_EQ(ptr2, new_ptr_1); - void* new_ptr_2 = allocator.root()->Alloc(size, type_name); - PA_EXPECT_PTR_EQ(ptr3, new_ptr_2); - - allocator.root()->Free(new_ptr_1); - allocator.root()->Free(new_ptr_2); - allocator.root()->Free(ptr4); - -#if BUILDFLAG(PA_EXPENSIVE_DCHECKS_ARE_ON) - // |SlotSpanMetadata::Free| must poison the slot's contents with - // |kFreedByte|. - EXPECT_EQ(kFreedByte, - *(static_cast<unsigned char*>(new_ptr_1) + (size - 1))); -#endif - } - - // Can we allocate a massive (128MB) size? - // Add +1, to test for cookie writing alignment issues. - // Test this only if the device has enough memory or it might fail due - // to OOM. - if (IsLargeMemoryDevice()) { - void* ptr = allocator.root()->Alloc(128 * 1024 * 1024 + 1, type_name); - allocator.root()->Free(ptr); - } - - { - // Check a more reasonable, but still direct mapped, size. - // Chop a system page and a byte off to test for rounding errors. - size_t size = 20 * 1024 * 1024; - ASSERT_GT(size, kMaxBucketed); - size -= SystemPageSize(); - size -= 1; - void* ptr = allocator.root()->Alloc(size, type_name); - char* char_ptr = static_cast<char*>(ptr); - *(char_ptr + (size - 1)) = 'A'; - allocator.root()->Free(ptr); - - // Can we free null? - allocator.root()->Free(nullptr); - - // Do we correctly get a null for a failed allocation? - EXPECT_EQ(nullptr, - allocator.root()->AllocWithFlags( - AllocFlags::kReturnNull, 3u * 1024 * 1024 * 1024, type_name)); - } -} - -// Test that we can fetch the real allocated size after an allocation. -TEST_P(PartitionAllocTest, AllocGetSizeAndStart) { - void* ptr; - size_t requested_size, actual_capacity, predicted_capacity; - - // Allocate something small. - requested_size = 511 - ExtraAllocSize(allocator); - predicted_capacity = - allocator.root()->AllocationCapacityFromRequestedSize(requested_size); - ptr = allocator.root()->Alloc(requested_size, type_name); - EXPECT_TRUE(ptr); - uintptr_t slot_start = allocator.root()->ObjectToSlotStart(ptr); - actual_capacity = - allocator.root()->AllocationCapacityFromSlotStart(slot_start); - EXPECT_EQ(predicted_capacity, actual_capacity); - EXPECT_LT(requested_size, actual_capacity); -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - if (UseBRPPool()) { - uintptr_t address = UntagPtr(ptr); - for (size_t offset = 0; offset < requested_size; ++offset) { - EXPECT_EQ(PartitionAllocGetSlotStartInBRPPool(address + offset), - slot_start); - } - } -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - allocator.root()->Free(ptr); - - // Allocate a size that should be a perfect match for a bucket, because it - // is an exact power of 2. - requested_size = (256 * 1024) - ExtraAllocSize(allocator); - predicted_capacity = - allocator.root()->AllocationCapacityFromRequestedSize(requested_size); - ptr = allocator.root()->Alloc(requested_size, type_name); - EXPECT_TRUE(ptr); - slot_start = allocator.root()->ObjectToSlotStart(ptr); - actual_capacity = - allocator.root()->AllocationCapacityFromSlotStart(slot_start); - EXPECT_EQ(predicted_capacity, actual_capacity); - EXPECT_EQ(requested_size, actual_capacity); -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - if (UseBRPPool()) { - uintptr_t address = UntagPtr(ptr); - for (size_t offset = 0; offset < requested_size; offset += 877) { - EXPECT_EQ(PartitionAllocGetSlotStartInBRPPool(address + offset), - slot_start); - } - } -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - allocator.root()->Free(ptr); - - // Allocate a size that is a system page smaller than a bucket. - // AllocationCapacityFromSlotStart() should return a larger size than we asked - // for now. - size_t num = 64; - while (num * SystemPageSize() >= 1024 * 1024) { - num /= 2; - } - requested_size = - num * SystemPageSize() - SystemPageSize() - ExtraAllocSize(allocator); - predicted_capacity = - allocator.root()->AllocationCapacityFromRequestedSize(requested_size); - ptr = allocator.root()->Alloc(requested_size, type_name); - EXPECT_TRUE(ptr); - slot_start = allocator.root()->ObjectToSlotStart(ptr); - actual_capacity = - allocator.root()->AllocationCapacityFromSlotStart(slot_start); - EXPECT_EQ(predicted_capacity, actual_capacity); - EXPECT_EQ(requested_size + SystemPageSize(), actual_capacity); -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - if (UseBRPPool()) { - uintptr_t address = UntagPtr(ptr); - for (size_t offset = 0; offset < requested_size; offset += 4999) { - EXPECT_EQ(PartitionAllocGetSlotStartInBRPPool(address + offset), - slot_start); - } - } -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - allocator.root()->Free(ptr); - - // Allocate the maximum allowed bucketed size. - requested_size = kMaxBucketed - ExtraAllocSize(allocator); - predicted_capacity = - allocator.root()->AllocationCapacityFromRequestedSize(requested_size); - ptr = allocator.root()->Alloc(requested_size, type_name); - EXPECT_TRUE(ptr); - slot_start = allocator.root()->ObjectToSlotStart(ptr); - actual_capacity = - allocator.root()->AllocationCapacityFromSlotStart(slot_start); - EXPECT_EQ(predicted_capacity, actual_capacity); - EXPECT_EQ(requested_size, actual_capacity); -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - if (UseBRPPool()) { - uintptr_t address = UntagPtr(ptr); - for (size_t offset = 0; offset < requested_size; offset += 4999) { - EXPECT_EQ(PartitionAllocGetSlotStartInBRPPool(address + offset), - slot_start); - } - } -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - - // Check that we can write at the end of the reported size too. - char* char_ptr = static_cast<char*>(ptr); - *(char_ptr + (actual_capacity - 1)) = 'A'; - allocator.root()->Free(ptr); - - // Allocate something very large, and uneven. - if (IsLargeMemoryDevice()) { - requested_size = 128 * 1024 * 1024 - 33; - predicted_capacity = - allocator.root()->AllocationCapacityFromRequestedSize(requested_size); - ptr = allocator.root()->Alloc(requested_size, type_name); - EXPECT_TRUE(ptr); - slot_start = allocator.root()->ObjectToSlotStart(ptr); - actual_capacity = - allocator.root()->AllocationCapacityFromSlotStart(slot_start); - EXPECT_EQ(predicted_capacity, actual_capacity); - - EXPECT_LT(requested_size, actual_capacity); - -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - if (UseBRPPool()) { - uintptr_t address = UntagPtr(ptr); - for (size_t offset = 0; offset < requested_size; offset += 16111) { - EXPECT_EQ(PartitionAllocGetSlotStartInBRPPool(address + offset), - slot_start); - } - } -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - allocator.root()->Free(ptr); - } - - // Too large allocation. - requested_size = MaxDirectMapped() + 1; - predicted_capacity = - allocator.root()->AllocationCapacityFromRequestedSize(requested_size); - EXPECT_EQ(requested_size, predicted_capacity); -} - -#if PA_CONFIG(HAS_MEMORY_TAGGING) -TEST_P(PartitionAllocTest, MTEProtectsFreedPtr) { - // This test checks that Arm's memory tagging extension (MTE) is correctly - // protecting freed pointers. - base::CPU cpu; - if (!cpu.has_mte()) { - // This test won't pass without MTE support. - GTEST_SKIP(); - } - - // Create an arbitrarily-sized small allocation. - size_t alloc_size = 64 - ExtraAllocSize(allocator); - uint64_t* ptr1 = - static_cast<uint64_t*>(allocator.root()->Alloc(alloc_size, type_name)); - EXPECT_TRUE(ptr1); - - // Invalidate the pointer by freeing it. - allocator.root()->Free(ptr1); - - // When we immediately reallocate a pointer, we should see the same allocation - // slot but with a different tag (PA_EXPECT_PTR_EQ ignores the MTE tag). - uint64_t* ptr2 = - static_cast<uint64_t*>(allocator.root()->Alloc(alloc_size, type_name)); - PA_EXPECT_PTR_EQ(ptr1, ptr2); - // The different tag bits mean that ptr1 is not the same as ptr2. - EXPECT_NE(ptr1, ptr2); - - // When we free again, we expect a new tag for that area that's different from - // ptr1 and ptr2. - allocator.root()->Free(ptr2); - uint64_t* ptr3 = - static_cast<uint64_t*>(allocator.root()->Alloc(alloc_size, type_name)); - PA_EXPECT_PTR_EQ(ptr2, ptr3); - EXPECT_NE(ptr1, ptr3); - EXPECT_NE(ptr2, ptr3); - - // We don't check anything about ptr3, but we do clean it up to avoid DCHECKs. - allocator.root()->Free(ptr3); -} -#endif // PA_CONFIG(HAS_MEMORY_TAGGING) - -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) -TEST_P(PartitionAllocTest, IsPtrWithinSameAlloc) { - if (!UseBRPPool()) { - return; - } - - const size_t kMinReasonableTestSize = - partition_alloc::internal::base::bits::AlignUp( - ExtraAllocSize(allocator) + 1, kAlignment); - ASSERT_GT(kMinReasonableTestSize, ExtraAllocSize(allocator)); - const size_t kSizes[] = {kMinReasonableTestSize, - 256, - SystemPageSize(), - PartitionPageSize(), - MaxRegularSlotSpanSize(), - MaxRegularSlotSpanSize() + 1, - MaxRegularSlotSpanSize() + SystemPageSize(), - MaxRegularSlotSpanSize() + PartitionPageSize(), - kMaxBucketed, - kMaxBucketed + 1, - kMaxBucketed + SystemPageSize(), - kMaxBucketed + PartitionPageSize(), - kSuperPageSize}; -#if BUILDFLAG(HAS_64_BIT_POINTERS) - constexpr size_t kFarFarAwayDelta = 512 * kGiB; -#else - constexpr size_t kFarFarAwayDelta = kGiB; -#endif - for (size_t size : kSizes) { - size_t requested_size = size - ExtraAllocSize(allocator); - // For regular slot-span allocations, confirm the size fills the entire - // slot. Otherwise the test would be ineffective, as Partition Alloc has no - // ability to check against the actual allocated size. - // Single-slot slot-spans and direct map don't have that problem. - if (size <= MaxRegularSlotSpanSize()) { - ASSERT_EQ(requested_size, - allocator.root()->AllocationCapacityFromRequestedSize( - requested_size)); - } - - constexpr size_t kNumRepeats = 3; - void* ptrs[kNumRepeats]; - for (void*& ptr : ptrs) { - ptr = allocator.root()->Alloc(requested_size, type_name); - // Double check. - if (size <= MaxRegularSlotSpanSize()) { - uintptr_t slot_start = allocator.root()->ObjectToSlotStart(ptr); - EXPECT_EQ( - requested_size, - allocator.root()->AllocationCapacityFromSlotStart(slot_start)); - } - - uintptr_t address = UntagPtr(ptr); - EXPECT_EQ(IsPtrWithinSameAlloc(address, address - kFarFarAwayDelta, 0u), - PtrPosWithinAlloc::kFarOOB); - EXPECT_EQ(IsPtrWithinSameAlloc(address, address - kSuperPageSize, 0u), - PtrPosWithinAlloc::kFarOOB); - EXPECT_EQ(IsPtrWithinSameAlloc(address, address - 1, 0u), - PtrPosWithinAlloc::kFarOOB); - EXPECT_EQ(IsPtrWithinSameAlloc(address, address, 0u), - PtrPosWithinAlloc::kInBounds); - EXPECT_EQ(IsPtrWithinSameAlloc(address, address + requested_size / 2, 0u), - PtrPosWithinAlloc::kInBounds); -#if BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR) - EXPECT_EQ(IsPtrWithinSameAlloc(address, address + requested_size - 1, 1u), - PtrPosWithinAlloc::kInBounds); - EXPECT_EQ(IsPtrWithinSameAlloc(address, address + requested_size, 1u), - PtrPosWithinAlloc::kAllocEnd); - EXPECT_EQ(IsPtrWithinSameAlloc(address, address + requested_size - 4, 4u), - PtrPosWithinAlloc::kInBounds); - for (size_t subtrahend = 0; subtrahend < 4; subtrahend++) { - EXPECT_EQ(IsPtrWithinSameAlloc( - address, address + requested_size - subtrahend, 4u), - PtrPosWithinAlloc::kAllocEnd); - } -#else // BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR) - EXPECT_EQ(IsPtrWithinSameAlloc(address, address + requested_size, 0u), - PtrPosWithinAlloc::kInBounds); -#endif - EXPECT_EQ(IsPtrWithinSameAlloc(address, address + requested_size + 1, 0u), - PtrPosWithinAlloc::kFarOOB); - EXPECT_EQ(IsPtrWithinSameAlloc( - address, address + requested_size + kSuperPageSize, 0u), - PtrPosWithinAlloc::kFarOOB); - EXPECT_EQ(IsPtrWithinSameAlloc( - address, address + requested_size + kFarFarAwayDelta, 0u), - PtrPosWithinAlloc::kFarOOB); - EXPECT_EQ( - IsPtrWithinSameAlloc(address + requested_size, - address + requested_size + kFarFarAwayDelta, 0u), - PtrPosWithinAlloc::kFarOOB); - EXPECT_EQ( - IsPtrWithinSameAlloc(address + requested_size, - address + requested_size + kSuperPageSize, 0u), - PtrPosWithinAlloc::kFarOOB); - EXPECT_EQ(IsPtrWithinSameAlloc(address + requested_size, - address + requested_size + 1, 0u), - PtrPosWithinAlloc::kFarOOB); -#if BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR) - EXPECT_EQ(IsPtrWithinSameAlloc(address + requested_size - 1, - address + requested_size - 1, 1u), - PtrPosWithinAlloc::kInBounds); - EXPECT_EQ(IsPtrWithinSameAlloc(address + requested_size - 1, - address + requested_size, 1u), - PtrPosWithinAlloc::kAllocEnd); - EXPECT_EQ(IsPtrWithinSameAlloc(address + requested_size, - address + requested_size, 1u), - PtrPosWithinAlloc::kAllocEnd); - EXPECT_EQ(IsPtrWithinSameAlloc(address + requested_size - 4, - address + requested_size - 4, 4u), - PtrPosWithinAlloc::kInBounds); - for (size_t addend = 1; addend < 4; addend++) { - EXPECT_EQ( - IsPtrWithinSameAlloc(address + requested_size - 4, - address + requested_size - 4 + addend, 4u), - PtrPosWithinAlloc::kAllocEnd); - } -#else // BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR) - EXPECT_EQ(IsPtrWithinSameAlloc(address + requested_size, - address + requested_size, 0u), - PtrPosWithinAlloc::kInBounds); -#endif - EXPECT_EQ(IsPtrWithinSameAlloc( - address + requested_size, - address + requested_size - (requested_size / 2), 0u), - PtrPosWithinAlloc::kInBounds); - EXPECT_EQ(IsPtrWithinSameAlloc(address + requested_size, address, 0u), - PtrPosWithinAlloc::kInBounds); - EXPECT_EQ(IsPtrWithinSameAlloc(address + requested_size, address - 1, 0u), - PtrPosWithinAlloc::kFarOOB); - EXPECT_EQ(IsPtrWithinSameAlloc(address + requested_size, - address - kSuperPageSize, 0u), - PtrPosWithinAlloc::kFarOOB); - EXPECT_EQ(IsPtrWithinSameAlloc(address + requested_size, - address - kFarFarAwayDelta, 0u), - PtrPosWithinAlloc::kFarOOB); - } - - for (void* ptr : ptrs) { - allocator.root()->Free(ptr); - } - } -} - -TEST_P(PartitionAllocTest, GetSlotStartMultiplePages) { - if (!UseBRPPool()) { - return; - } - - auto* root = allocator.root(); - // Find the smallest bucket with multiple PartitionPages. When searching for - // a bucket here, we need to check two conditions: - // (1) The bucket is used in our current bucket distribution. - // (2) The bucket is large enough that our requested size (see below) will be - // non-zero. - size_t real_size = 0; - for (const auto& bucket : root->buckets) { - if ((root->buckets + SizeToIndex(bucket.slot_size))->slot_size != - bucket.slot_size) { - continue; - } - if (bucket.slot_size <= ExtraAllocSize(allocator)) { - continue; - } - if (bucket.num_system_pages_per_slot_span > - NumSystemPagesPerPartitionPage()) { - real_size = bucket.slot_size; - break; - } - } - - // Make sure that we've managed to find an appropriate bucket. - ASSERT_GT(real_size, 0u); - - const size_t requested_size = real_size - ExtraAllocSize(allocator); - // Double check we don't end up with 0 or negative size. - EXPECT_GT(requested_size, 0u); - EXPECT_LE(requested_size, real_size); - const auto* bucket = allocator.root()->buckets + SizeToIndex(real_size); - EXPECT_EQ(bucket->slot_size, real_size); - // Make sure the test is testing multiple partition pages case. - EXPECT_GT(bucket->num_system_pages_per_slot_span, - PartitionPageSize() / SystemPageSize()); - size_t num_slots = - (bucket->num_system_pages_per_slot_span * SystemPageSize()) / real_size; - std::vector<void*> ptrs; - for (size_t i = 0; i < num_slots; ++i) { - ptrs.push_back(allocator.root()->Alloc(requested_size, type_name)); - } - for (void* ptr : ptrs) { - uintptr_t address = UntagPtr(ptr); - uintptr_t slot_start = allocator.root()->ObjectToSlotStart(ptr); - EXPECT_EQ(allocator.root()->AllocationCapacityFromSlotStart(slot_start), - requested_size); - for (size_t offset = 0; offset < requested_size; offset += 13) { - EXPECT_EQ(PartitionAllocGetSlotStartInBRPPool(address + offset), - slot_start); - } - allocator.root()->Free(ptr); - } -} -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - -// Test the realloc() contract. -TEST_P(PartitionAllocTest, Realloc) { - // realloc(0, size) should be equivalent to malloc(). - void* ptr = allocator.root()->Realloc(nullptr, kTestAllocSize, type_name); - memset(ptr, 'A', kTestAllocSize); - auto* slot_span = - SlotSpan::FromSlotStart(allocator.root()->ObjectToSlotStart(ptr)); - // realloc(ptr, 0) should be equivalent to free(). - void* ptr2 = allocator.root()->Realloc(ptr, 0, type_name); - EXPECT_EQ(nullptr, ptr2); - EXPECT_EQ(allocator.root()->ObjectToSlotStart(ptr), - UntagPtr(slot_span->get_freelist_head())); - - // Test that growing an allocation with realloc() copies everything from the - // old allocation. - size_t size = SystemPageSize() - ExtraAllocSize(allocator); - // Confirm size fills the entire slot. - ASSERT_EQ(size, allocator.root()->AllocationCapacityFromRequestedSize(size)); - ptr = allocator.root()->Alloc(size, type_name); - memset(ptr, 'A', size); - ptr2 = allocator.root()->Realloc(ptr, size + 1, type_name); - PA_EXPECT_PTR_NE(ptr, ptr2); - char* char_ptr2 = static_cast<char*>(ptr2); - EXPECT_EQ('A', char_ptr2[0]); - EXPECT_EQ('A', char_ptr2[size - 1]); -#if BUILDFLAG(PA_EXPENSIVE_DCHECKS_ARE_ON) - EXPECT_EQ(kUninitializedByte, static_cast<unsigned char>(char_ptr2[size])); -#endif - - // Test that shrinking an allocation with realloc() also copies everything - // from the old allocation. Use |size - 1| to test what happens to the extra - // space before the cookie. - ptr = allocator.root()->Realloc(ptr2, size - 1, type_name); - PA_EXPECT_PTR_NE(ptr2, ptr); - char* char_ptr = static_cast<char*>(ptr); - EXPECT_EQ('A', char_ptr[0]); - EXPECT_EQ('A', char_ptr[size - 2]); -#if BUILDFLAG(PA_EXPENSIVE_DCHECKS_ARE_ON) - EXPECT_EQ(kUninitializedByte, static_cast<unsigned char>(char_ptr[size - 1])); -#endif - - allocator.root()->Free(ptr); - - // Single-slot slot spans... - // Test that growing an allocation with realloc() copies everything from the - // old allocation. - size = MaxRegularSlotSpanSize() + 1; - ASSERT_LE(2 * size, kMaxBucketed); // should be in single-slot span range - // Confirm size doesn't fill the entire slot. - ASSERT_LT(size, allocator.root()->AllocationCapacityFromRequestedSize(size)); - ptr = allocator.root()->Alloc(size, type_name); - memset(ptr, 'A', size); - ptr2 = allocator.root()->Realloc(ptr, size * 2, type_name); - PA_EXPECT_PTR_NE(ptr, ptr2); - char_ptr2 = static_cast<char*>(ptr2); - EXPECT_EQ('A', char_ptr2[0]); - EXPECT_EQ('A', char_ptr2[size - 1]); -#if BUILDFLAG(PA_EXPENSIVE_DCHECKS_ARE_ON) - EXPECT_EQ(kUninitializedByte, static_cast<unsigned char>(char_ptr2[size])); -#endif - allocator.root()->Free(ptr2); - - // Test that shrinking an allocation with realloc() also copies everything - // from the old allocation. - size = 2 * (MaxRegularSlotSpanSize() + 1); - ASSERT_GT(size / 2, MaxRegularSlotSpanSize()); // in single-slot span range - ptr = allocator.root()->Alloc(size, type_name); - memset(ptr, 'A', size); - ptr2 = allocator.root()->Realloc(ptr2, size / 2, type_name); - PA_EXPECT_PTR_NE(ptr, ptr2); - char_ptr2 = static_cast<char*>(ptr2); - EXPECT_EQ('A', char_ptr2[0]); - EXPECT_EQ('A', char_ptr2[size / 2 - 1]); -#if BUILDFLAG(PA_DCHECK_IS_ON) - // For single-slot slot spans, the cookie is always placed immediately after - // the allocation. - EXPECT_EQ(kCookieValue[0], static_cast<unsigned char>(char_ptr2[size / 2])); -#endif - allocator.root()->Free(ptr2); - - // Test that shrinking a direct mapped allocation happens in-place. - // Pick a large size so that Realloc doesn't think it's worthwhile to - // downsize even if one less super page is used (due to high granularity on - // 64-bit systems). - size = 10 * kSuperPageSize + SystemPageSize() - 42; - ASSERT_GT(size - 32 * SystemPageSize(), kMaxBucketed); - ptr = allocator.root()->Alloc(size, type_name); - uintptr_t slot_start = allocator.root()->ObjectToSlotStart(ptr); - size_t actual_capacity = - allocator.root()->AllocationCapacityFromSlotStart(slot_start); - ptr2 = allocator.root()->Realloc(ptr, size - SystemPageSize(), type_name); - uintptr_t slot_start2 = allocator.root()->ObjectToSlotStart(ptr2); - EXPECT_EQ(slot_start, slot_start2); - EXPECT_EQ(actual_capacity - SystemPageSize(), - allocator.root()->AllocationCapacityFromSlotStart(slot_start2)); - void* ptr3 = - allocator.root()->Realloc(ptr2, size - 32 * SystemPageSize(), type_name); - uintptr_t slot_start3 = allocator.root()->ObjectToSlotStart(ptr3); - EXPECT_EQ(slot_start2, slot_start3); - EXPECT_EQ(actual_capacity - 32 * SystemPageSize(), - allocator.root()->AllocationCapacityFromSlotStart(slot_start3)); - - // Test that a previously in-place shrunk direct mapped allocation can be - // expanded up again up to its original size. - ptr = allocator.root()->Realloc(ptr3, size, type_name); - slot_start = allocator.root()->ObjectToSlotStart(ptr); - EXPECT_EQ(slot_start3, slot_start); - EXPECT_EQ(actual_capacity, - allocator.root()->AllocationCapacityFromSlotStart(slot_start)); - - // Test that the allocation can be expanded in place up to its capacity. - ptr2 = allocator.root()->Realloc(ptr, actual_capacity, type_name); - slot_start2 = allocator.root()->ObjectToSlotStart(ptr2); - EXPECT_EQ(slot_start, slot_start2); - EXPECT_EQ(actual_capacity, - allocator.root()->AllocationCapacityFromSlotStart(slot_start2)); - - // Test that a direct mapped allocation is performed not in-place when the - // new size is small enough. - ptr3 = allocator.root()->Realloc(ptr2, SystemPageSize(), type_name); - slot_start3 = allocator.root()->ObjectToSlotStart(ptr3); - EXPECT_NE(slot_start, slot_start3); - - allocator.root()->Free(ptr3); -} - -TEST_P(PartitionAllocTest, ReallocDirectMapAligned) { - size_t alignments[] = { - PartitionPageSize(), - 2 * PartitionPageSize(), - kMaxSupportedAlignment / 2, - kMaxSupportedAlignment, - }; - - for (size_t alignment : alignments) { - // Test that shrinking a direct mapped allocation happens in-place. - // Pick a large size so that Realloc doesn't think it's worthwhile to - // downsize even if one less super page is used (due to high granularity on - // 64-bit systems), even if the alignment padding is taken out. - size_t size = 10 * kSuperPageSize + SystemPageSize() - 42; - ASSERT_GT(size, kMaxBucketed); - void* ptr = - allocator.root()->AllocWithFlagsInternal(0, size, alignment, type_name); - uintptr_t slot_start = allocator.root()->ObjectToSlotStart(ptr); - size_t actual_capacity = - allocator.root()->AllocationCapacityFromSlotStart(slot_start); - void* ptr2 = - allocator.root()->Realloc(ptr, size - SystemPageSize(), type_name); - uintptr_t slot_start2 = allocator.root()->ObjectToSlotStart(ptr2); - EXPECT_EQ(slot_start, slot_start2); - EXPECT_EQ(actual_capacity - SystemPageSize(), - allocator.root()->AllocationCapacityFromSlotStart(slot_start2)); - void* ptr3 = allocator.root()->Realloc(ptr2, size - 32 * SystemPageSize(), - type_name); - uintptr_t slot_start3 = allocator.root()->ObjectToSlotStart(ptr3); - EXPECT_EQ(slot_start2, slot_start3); - EXPECT_EQ(actual_capacity - 32 * SystemPageSize(), - allocator.root()->AllocationCapacityFromSlotStart(slot_start3)); - - // Test that a previously in-place shrunk direct mapped allocation can be - // expanded up again up to its original size. - ptr = allocator.root()->Realloc(ptr3, size, type_name); - slot_start = allocator.root()->ObjectToSlotStart(ptr); - EXPECT_EQ(slot_start3, slot_start); - EXPECT_EQ(actual_capacity, - allocator.root()->AllocationCapacityFromSlotStart(slot_start)); - - // Test that the allocation can be expanded in place up to its capacity. - ptr2 = allocator.root()->Realloc(ptr, actual_capacity, type_name); - slot_start2 = allocator.root()->ObjectToSlotStart(ptr2); - EXPECT_EQ(slot_start, slot_start2); - EXPECT_EQ(actual_capacity, - allocator.root()->AllocationCapacityFromSlotStart(slot_start2)); - - // Test that a direct mapped allocation is performed not in-place when the - // new size is small enough. - ptr3 = allocator.root()->Realloc(ptr2, SystemPageSize(), type_name); - slot_start3 = allocator.root()->ObjectToSlotStart(ptr3); - EXPECT_NE(slot_start2, slot_start3); - - allocator.root()->Free(ptr3); - } -} - -TEST_P(PartitionAllocTest, ReallocDirectMapAlignedRelocate) { - // Pick size such that the alignment will put it cross the super page - // boundary. - size_t size = 2 * kSuperPageSize - kMaxSupportedAlignment + SystemPageSize(); - ASSERT_GT(size, kMaxBucketed); - void* ptr = allocator.root()->AllocWithFlagsInternal( - 0, size, kMaxSupportedAlignment, type_name); - // Reallocating with the same size will actually relocate, because without a - // need for alignment we can downsize the reservation significantly. - void* ptr2 = allocator.root()->Realloc(ptr, size, type_name); - PA_EXPECT_PTR_NE(ptr, ptr2); - allocator.root()->Free(ptr2); - - // Again pick size such that the alignment will put it cross the super page - // boundary, but this time make it so large that Realloc doesn't fing it worth - // shrinking. - size = 10 * kSuperPageSize - kMaxSupportedAlignment + SystemPageSize(); - ASSERT_GT(size, kMaxBucketed); - ptr = allocator.root()->AllocWithFlagsInternal( - 0, size, kMaxSupportedAlignment, type_name); - ptr2 = allocator.root()->Realloc(ptr, size, type_name); - EXPECT_EQ(ptr, ptr2); - allocator.root()->Free(ptr2); -} - -// Tests the handing out of freelists for partial slot spans. -TEST_P(PartitionAllocTest, PartialPageFreelists) { - size_t big_size = SystemPageSize() - ExtraAllocSize(allocator); - size_t bucket_index = SizeToIndex(big_size + ExtraAllocSize(allocator)); - PartitionRoot<ThreadSafe>::Bucket* bucket = - &allocator.root()->buckets[bucket_index]; - EXPECT_EQ(nullptr, bucket->empty_slot_spans_head); - - void* ptr = allocator.root()->Alloc(big_size, type_name); - EXPECT_TRUE(ptr); - - auto* slot_span = - SlotSpan::FromSlotStart(allocator.root()->ObjectToSlotStart(ptr)); - size_t total_slots = - (slot_span->bucket->num_system_pages_per_slot_span * SystemPageSize()) / - (big_size + ExtraAllocSize(allocator)); - EXPECT_EQ(4u, total_slots); - // The freelist should have one entry, because we were able to exactly fit - // one object slot and one freelist pointer (the null that the head points - // to) into a system page. - EXPECT_FALSE(slot_span->get_freelist_head()); - EXPECT_EQ(1u, slot_span->num_allocated_slots); - EXPECT_EQ(3u, slot_span->num_unprovisioned_slots); - - void* ptr2 = allocator.root()->Alloc(big_size, type_name); - EXPECT_TRUE(ptr2); - EXPECT_FALSE(slot_span->get_freelist_head()); - EXPECT_EQ(2u, slot_span->num_allocated_slots); - EXPECT_EQ(2u, slot_span->num_unprovisioned_slots); - - void* ptr3 = allocator.root()->Alloc(big_size, type_name); - EXPECT_TRUE(ptr3); - EXPECT_FALSE(slot_span->get_freelist_head()); - EXPECT_EQ(3u, slot_span->num_allocated_slots); - EXPECT_EQ(1u, slot_span->num_unprovisioned_slots); - - void* ptr4 = allocator.root()->Alloc(big_size, type_name); - EXPECT_TRUE(ptr4); - EXPECT_FALSE(slot_span->get_freelist_head()); - EXPECT_EQ(4u, slot_span->num_allocated_slots); - EXPECT_EQ(0u, slot_span->num_unprovisioned_slots); - - void* ptr5 = allocator.root()->Alloc(big_size, type_name); - EXPECT_TRUE(ptr5); - - auto* slot_span2 = - SlotSpan::FromSlotStart(allocator.root()->ObjectToSlotStart(ptr5)); - EXPECT_EQ(1u, slot_span2->num_allocated_slots); - - // Churn things a little whilst there's a partial slot span freelist. - allocator.root()->Free(ptr); - ptr = allocator.root()->Alloc(big_size, type_name); - void* ptr6 = allocator.root()->Alloc(big_size, type_name); - - allocator.root()->Free(ptr); - allocator.root()->Free(ptr2); - allocator.root()->Free(ptr3); - allocator.root()->Free(ptr4); - allocator.root()->Free(ptr5); - allocator.root()->Free(ptr6); - EXPECT_TRUE(slot_span->in_empty_cache()); - EXPECT_TRUE(slot_span2->in_empty_cache()); - EXPECT_TRUE(slot_span2->get_freelist_head()); - EXPECT_EQ(0u, slot_span2->num_allocated_slots); - - // Size that's just above half a page. - size_t non_dividing_size = - SystemPageSize() / 2 + 1 - ExtraAllocSize(allocator); - bucket_index = SizeToIndex(non_dividing_size + ExtraAllocSize(allocator)); - bucket = &allocator.root()->buckets[bucket_index]; - EXPECT_EQ(nullptr, bucket->empty_slot_spans_head); - - ptr = allocator.root()->Alloc(non_dividing_size, type_name); - EXPECT_TRUE(ptr); - - slot_span = SlotSpan::FromSlotStart(allocator.root()->ObjectToSlotStart(ptr)); - total_slots = - (slot_span->bucket->num_system_pages_per_slot_span * SystemPageSize()) / - bucket->slot_size; - - EXPECT_FALSE(slot_span->get_freelist_head()); - EXPECT_EQ(1u, slot_span->num_allocated_slots); - EXPECT_EQ(total_slots - 1, slot_span->num_unprovisioned_slots); - - ptr2 = allocator.root()->Alloc(non_dividing_size, type_name); - EXPECT_TRUE(ptr2); - EXPECT_TRUE(slot_span->get_freelist_head()); - EXPECT_EQ(2u, slot_span->num_allocated_slots); - // 2 slots got provisioned: the first one fills the rest of the first (already - // provision page) and exceeds it by just a tad, thus leading to provisioning - // a new page, and the second one fully fits within that new page. - EXPECT_EQ(total_slots - 3, slot_span->num_unprovisioned_slots); - - ptr3 = allocator.root()->Alloc(non_dividing_size, type_name); - EXPECT_TRUE(ptr3); - EXPECT_FALSE(slot_span->get_freelist_head()); - EXPECT_EQ(3u, slot_span->num_allocated_slots); - EXPECT_EQ(total_slots - 3, slot_span->num_unprovisioned_slots); - - allocator.root()->Free(ptr); - allocator.root()->Free(ptr2); - allocator.root()->Free(ptr3); - EXPECT_TRUE(slot_span->in_empty_cache()); - EXPECT_TRUE(slot_span2->get_freelist_head()); - EXPECT_EQ(0u, slot_span2->num_allocated_slots); - - // And test a couple of sizes that do not cross SystemPageSize() with a - // single allocation. - size_t medium_size = (SystemPageSize() / 2) - ExtraAllocSize(allocator); - bucket_index = SizeToIndex(medium_size + ExtraAllocSize(allocator)); - bucket = &allocator.root()->buckets[bucket_index]; - EXPECT_EQ(nullptr, bucket->empty_slot_spans_head); - - ptr = allocator.root()->Alloc(medium_size, type_name); - EXPECT_TRUE(ptr); - slot_span = SlotSpan::FromSlotStart(allocator.root()->ObjectToSlotStart(ptr)); - EXPECT_EQ(1u, slot_span->num_allocated_slots); - total_slots = - (slot_span->bucket->num_system_pages_per_slot_span * SystemPageSize()) / - (medium_size + ExtraAllocSize(allocator)); - size_t first_slot_span_slots = - SystemPageSize() / (medium_size + ExtraAllocSize(allocator)); - EXPECT_EQ(2u, first_slot_span_slots); - EXPECT_EQ(total_slots - first_slot_span_slots, - slot_span->num_unprovisioned_slots); - - allocator.root()->Free(ptr); - - size_t small_size = (SystemPageSize() / 4) - ExtraAllocSize(allocator); - bucket_index = SizeToIndex(small_size + ExtraAllocSize(allocator)); - bucket = &allocator.root()->buckets[bucket_index]; - EXPECT_EQ(nullptr, bucket->empty_slot_spans_head); - - ptr = allocator.root()->Alloc(small_size, type_name); - EXPECT_TRUE(ptr); - slot_span = SlotSpan::FromSlotStart(allocator.root()->ObjectToSlotStart(ptr)); - EXPECT_EQ(1u, slot_span->num_allocated_slots); - total_slots = - (slot_span->bucket->num_system_pages_per_slot_span * SystemPageSize()) / - (small_size + ExtraAllocSize(allocator)); - first_slot_span_slots = - SystemPageSize() / (small_size + ExtraAllocSize(allocator)); - EXPECT_EQ(total_slots - first_slot_span_slots, - slot_span->num_unprovisioned_slots); - - allocator.root()->Free(ptr); - EXPECT_TRUE(slot_span->get_freelist_head()); - EXPECT_EQ(0u, slot_span->num_allocated_slots); - - static_assert(kExtraAllocSizeWithRefCount < 64, ""); - size_t very_small_size = (ExtraAllocSize(allocator) <= 32) - ? (32 - ExtraAllocSize(allocator)) - : (64 - ExtraAllocSize(allocator)); - size_t very_small_adjusted_size = - allocator.root()->AdjustSize0IfNeeded(very_small_size); - bucket_index = - SizeToIndex(very_small_adjusted_size + ExtraAllocSize(allocator)); - bucket = &allocator.root()->buckets[bucket_index]; - EXPECT_EQ(nullptr, bucket->empty_slot_spans_head); - - ptr = allocator.root()->Alloc(very_small_size, type_name); - EXPECT_TRUE(ptr); - slot_span = SlotSpan::FromSlotStart(allocator.root()->ObjectToSlotStart(ptr)); - EXPECT_EQ(1u, slot_span->num_allocated_slots); - size_t very_small_actual_size = allocator.root()->GetUsableSize(ptr); - total_slots = - (slot_span->bucket->num_system_pages_per_slot_span * SystemPageSize()) / - (very_small_actual_size + ExtraAllocSize(allocator)); - first_slot_span_slots = - SystemPageSize() / (very_small_actual_size + ExtraAllocSize(allocator)); - EXPECT_EQ(total_slots - first_slot_span_slots, - slot_span->num_unprovisioned_slots); - - allocator.root()->Free(ptr); - EXPECT_TRUE(slot_span->get_freelist_head()); - EXPECT_EQ(0u, slot_span->num_allocated_slots); - - // And try an allocation size (against the generic allocator) that is - // larger than a system page. - size_t page_and_a_half_size = - (SystemPageSize() + (SystemPageSize() / 2)) - ExtraAllocSize(allocator); - ptr = allocator.root()->Alloc(page_and_a_half_size, type_name); - EXPECT_TRUE(ptr); - slot_span = SlotSpan::FromSlotStart(allocator.root()->ObjectToSlotStart(ptr)); - EXPECT_EQ(1u, slot_span->num_allocated_slots); - // Only the first slot was provisioned, and that's the one that was just - // allocated so the free list is empty. - EXPECT_TRUE(!slot_span->get_freelist_head()); - total_slots = - (slot_span->bucket->num_system_pages_per_slot_span * SystemPageSize()) / - (page_and_a_half_size + ExtraAllocSize(allocator)); - EXPECT_EQ(total_slots - 1, slot_span->num_unprovisioned_slots); - ptr2 = allocator.root()->Alloc(page_and_a_half_size, type_name); - EXPECT_TRUE(ptr); - slot_span = SlotSpan::FromSlotStart(allocator.root()->ObjectToSlotStart(ptr)); - EXPECT_EQ(2u, slot_span->num_allocated_slots); - // As above, only one slot was provisioned. - EXPECT_TRUE(!slot_span->get_freelist_head()); - EXPECT_EQ(total_slots - 2, slot_span->num_unprovisioned_slots); - allocator.root()->Free(ptr); - allocator.root()->Free(ptr2); - - // And then make sure than exactly the page size only faults one page. - size_t page_size = SystemPageSize() - ExtraAllocSize(allocator); - ptr = allocator.root()->Alloc(page_size, type_name); - EXPECT_TRUE(ptr); - slot_span = SlotSpan::FromSlotStart(allocator.root()->ObjectToSlotStart(ptr)); - EXPECT_EQ(1u, slot_span->num_allocated_slots); - EXPECT_TRUE(slot_span->get_freelist_head()); - total_slots = - (slot_span->bucket->num_system_pages_per_slot_span * SystemPageSize()) / - (page_size + ExtraAllocSize(allocator)); - EXPECT_EQ(total_slots - 2, slot_span->num_unprovisioned_slots); - allocator.root()->Free(ptr); -} - -// Test some of the fragmentation-resistant properties of the allocator. -TEST_P(PartitionAllocTest, SlotSpanRefilling) { - PartitionRoot<ThreadSafe>::Bucket* bucket = - &allocator.root()->buckets[test_bucket_index_]; - - // Grab two full slot spans and a non-full slot span. - auto* slot_span1 = GetFullSlotSpan(kTestAllocSize); - auto* slot_span2 = GetFullSlotSpan(kTestAllocSize); - void* ptr = allocator.root()->Alloc(kTestAllocSize, type_name); - EXPECT_TRUE(ptr); - EXPECT_NE(slot_span1, bucket->active_slot_spans_head); - EXPECT_NE(slot_span2, bucket->active_slot_spans_head); - auto* slot_span = - SlotSpan::FromSlotStart(allocator.root()->ObjectToSlotStart(ptr)); - EXPECT_EQ(1u, slot_span->num_allocated_slots); - - // Work out a pointer into slot_span2 and free it; and then slot_span1 and - // free it. - void* ptr2 = allocator.root()->SlotStartToObject( - SlotSpan::ToSlotSpanStart(slot_span1)); - allocator.root()->Free(ptr2); - ptr2 = allocator.root()->SlotStartToObject( - SlotSpan::ToSlotSpanStart(slot_span2)); - allocator.root()->Free(ptr2); - - // If we perform two allocations from the same bucket now, we expect to - // refill both the nearly full slot spans. - std::ignore = allocator.root()->Alloc(kTestAllocSize, type_name); - std::ignore = allocator.root()->Alloc(kTestAllocSize, type_name); - EXPECT_EQ(1u, slot_span->num_allocated_slots); - - FreeFullSlotSpan(allocator.root(), slot_span2); - FreeFullSlotSpan(allocator.root(), slot_span1); - allocator.root()->Free(ptr); -} - -// Basic tests to ensure that allocations work for partial page buckets. -TEST_P(PartitionAllocTest, PartialPages) { - // Find a size that is backed by a partial partition page. - size_t size = sizeof(void*); - size_t bucket_index; - - PartitionRoot<ThreadSafe>::Bucket* bucket = nullptr; - constexpr size_t kMaxSize = 4000u; - while (size < kMaxSize) { - bucket_index = SizeToIndex(size + ExtraAllocSize(allocator)); - bucket = &allocator.root()->buckets[bucket_index]; - if (bucket->num_system_pages_per_slot_span % - NumSystemPagesPerPartitionPage()) { - break; - } - size += sizeof(void*); - } - EXPECT_LT(size, kMaxSize); - - auto* slot_span1 = GetFullSlotSpan(size); - auto* slot_span2 = GetFullSlotSpan(size); - FreeFullSlotSpan(allocator.root(), slot_span2); - FreeFullSlotSpan(allocator.root(), slot_span1); -} - -// Test correct handling if our mapping collides with another. -TEST_P(PartitionAllocTest, MappingCollision) { - size_t num_pages_per_slot_span = GetNumPagesPerSlotSpan(kTestAllocSize); - // The -2 is because the first and last partition pages in a super page are - // guard pages. We also discount the partition pages used for the tag bitmap. - size_t num_slot_span_needed = - (NumPartitionPagesPerSuperPage() - 2 - - partition_alloc::internal::NumPartitionPagesPerFreeSlotBitmap()) / - num_pages_per_slot_span; - size_t num_partition_pages_needed = - num_slot_span_needed * num_pages_per_slot_span; - - auto first_super_page_pages = - std::make_unique<SlotSpan*[]>(num_partition_pages_needed); - auto second_super_page_pages = - std::make_unique<SlotSpan*[]>(num_partition_pages_needed); - - size_t i; - for (i = 0; i < num_partition_pages_needed; ++i) { - first_super_page_pages[i] = GetFullSlotSpan(kTestAllocSize); - } - - uintptr_t slot_span_start = - SlotSpan::ToSlotSpanStart(first_super_page_pages[0]); - EXPECT_EQ(PartitionPageSize() + - partition_alloc::internal::ReservedFreeSlotBitmapSize(), - slot_span_start & kSuperPageOffsetMask); - uintptr_t super_page = - slot_span_start - PartitionPageSize() - - partition_alloc::internal::ReservedFreeSlotBitmapSize(); - // Map a single system page either side of the mapping for our allocations, - // with the goal of tripping up alignment of the next mapping. - uintptr_t map1 = - AllocPages(super_page - PageAllocationGranularity(), - PageAllocationGranularity(), PageAllocationGranularity(), - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kInaccessible), - PageTag::kPartitionAlloc); - EXPECT_TRUE(map1); - uintptr_t map2 = - AllocPages(super_page + kSuperPageSize, PageAllocationGranularity(), - PageAllocationGranularity(), - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kInaccessible), - PageTag::kPartitionAlloc); - EXPECT_TRUE(map2); - - for (i = 0; i < num_partition_pages_needed; ++i) { - second_super_page_pages[i] = GetFullSlotSpan(kTestAllocSize); - } - - FreePages(map1, PageAllocationGranularity()); - FreePages(map2, PageAllocationGranularity()); - - super_page = SlotSpan::ToSlotSpanStart(second_super_page_pages[0]); - EXPECT_EQ(PartitionPageSize() + - partition_alloc::internal::ReservedFreeSlotBitmapSize(), - super_page & kSuperPageOffsetMask); - super_page -= PartitionPageSize() + - partition_alloc::internal::ReservedFreeSlotBitmapSize(); - // Map a single system page either side of the mapping for our allocations, - // with the goal of tripping up alignment of the next mapping. - map1 = AllocPages(super_page - PageAllocationGranularity(), - PageAllocationGranularity(), PageAllocationGranularity(), - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kReadWriteTagged), - PageTag::kPartitionAlloc); - EXPECT_TRUE(map1); - map2 = AllocPages(super_page + kSuperPageSize, PageAllocationGranularity(), - PageAllocationGranularity(), - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kReadWriteTagged), - PageTag::kPartitionAlloc); - EXPECT_TRUE(map2); - EXPECT_TRUE(TrySetSystemPagesAccess( - map1, PageAllocationGranularity(), - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kInaccessible))); - EXPECT_TRUE(TrySetSystemPagesAccess( - map2, PageAllocationGranularity(), - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kInaccessible))); - - auto* slot_span_in_third_super_page = GetFullSlotSpan(kTestAllocSize); - FreePages(map1, PageAllocationGranularity()); - FreePages(map2, PageAllocationGranularity()); - - EXPECT_EQ(0u, SlotSpan::ToSlotSpanStart(slot_span_in_third_super_page) & - PartitionPageOffsetMask()); - - // And make sure we really did get a page in a new superpage. - EXPECT_NE( - SlotSpan::ToSlotSpanStart(first_super_page_pages[0]) & kSuperPageBaseMask, - SlotSpan::ToSlotSpanStart(slot_span_in_third_super_page) & - kSuperPageBaseMask); - EXPECT_NE(SlotSpan::ToSlotSpanStart(second_super_page_pages[0]) & - kSuperPageBaseMask, - SlotSpan::ToSlotSpanStart(slot_span_in_third_super_page) & - kSuperPageBaseMask); - - FreeFullSlotSpan(allocator.root(), slot_span_in_third_super_page); - for (i = 0; i < num_partition_pages_needed; ++i) { - FreeFullSlotSpan(allocator.root(), first_super_page_pages[i]); - FreeFullSlotSpan(allocator.root(), second_super_page_pages[i]); - } -} - -// Tests that slot spans in the free slot span cache do get freed as -// appropriate. -TEST_P(PartitionAllocTest, FreeCache) { - EXPECT_EQ(0U, allocator.root()->get_total_size_of_committed_pages()); - - size_t big_size = 1000 - ExtraAllocSize(allocator); - size_t bucket_index = SizeToIndex(big_size + ExtraAllocSize(allocator)); - PartitionBucket<internal::ThreadSafe>* bucket = - &allocator.root()->buckets[bucket_index]; - - void* ptr = allocator.root()->Alloc(big_size, type_name); - EXPECT_TRUE(ptr); - auto* slot_span = - SlotSpan::FromSlotStart(allocator.root()->ObjectToSlotStart(ptr)); - EXPECT_EQ(nullptr, bucket->empty_slot_spans_head); - EXPECT_EQ(1u, slot_span->num_allocated_slots); - // Lazy commit commits only needed pages. - size_t expected_committed_size = - kUseLazyCommit ? SystemPageSize() : PartitionPageSize(); - EXPECT_EQ(expected_committed_size, - allocator.root()->get_total_size_of_committed_pages()); - allocator.root()->Free(ptr); - EXPECT_EQ(0u, slot_span->num_allocated_slots); - EXPECT_TRUE(slot_span->in_empty_cache()); - EXPECT_TRUE(slot_span->get_freelist_head()); - - CycleFreeCache(kTestAllocSize); - - // Flushing the cache should have really freed the unused slot spans. - EXPECT_FALSE(slot_span->get_freelist_head()); - EXPECT_FALSE(slot_span->in_empty_cache()); - EXPECT_EQ(0u, slot_span->num_allocated_slots); - size_t num_system_pages_per_slot_span = allocator.root() - ->buckets[test_bucket_index_] - .num_system_pages_per_slot_span; - size_t expected_size = - kUseLazyCommit ? SystemPageSize() - : num_system_pages_per_slot_span * SystemPageSize(); - EXPECT_EQ(expected_size, - allocator.root()->get_total_size_of_committed_pages()); - - // Check that an allocation works ok whilst in this state (a free'd slot span - // as the active slot spans head). - ptr = allocator.root()->Alloc(big_size, type_name); - EXPECT_FALSE(bucket->empty_slot_spans_head); - allocator.root()->Free(ptr); - - // Also check that a slot span that is bouncing immediately between empty and - // used does not get freed. - for (size_t i = 0; i < kMaxFreeableSpans * 2; ++i) { - ptr = allocator.root()->Alloc(big_size, type_name); - EXPECT_TRUE(slot_span->get_freelist_head()); - allocator.root()->Free(ptr); - EXPECT_TRUE(slot_span->get_freelist_head()); - } - EXPECT_EQ(expected_committed_size, - allocator.root()->get_total_size_of_committed_pages()); -} - -// Tests for a bug we had with losing references to free slot spans. -TEST_P(PartitionAllocTest, LostFreeSlotSpansBug) { - size_t size = PartitionPageSize() - ExtraAllocSize(allocator); - - void* ptr = allocator.root()->Alloc(size, type_name); - EXPECT_TRUE(ptr); - void* ptr2 = allocator.root()->Alloc(size, type_name); - EXPECT_TRUE(ptr2); - - SlotSpanMetadata<internal::ThreadSafe>* slot_span = - SlotSpanMetadata<internal::ThreadSafe>::FromSlotStart( - allocator.root()->ObjectToSlotStart(ptr)); - SlotSpanMetadata<internal::ThreadSafe>* slot_span2 = - SlotSpanMetadata<internal::ThreadSafe>::FromSlotStart( - allocator.root()->ObjectToSlotStart(ptr2)); - PartitionBucket<internal::ThreadSafe>* bucket = slot_span->bucket; - - EXPECT_EQ(nullptr, bucket->empty_slot_spans_head); - EXPECT_EQ(1u, slot_span->num_allocated_slots); - EXPECT_EQ(1u, slot_span2->num_allocated_slots); - EXPECT_TRUE(slot_span->is_full()); - EXPECT_TRUE(slot_span2->is_full()); - // The first span was kicked out from the active list, but the second one - // wasn't. - EXPECT_TRUE(slot_span->marked_full); - EXPECT_FALSE(slot_span2->marked_full); - - allocator.root()->Free(ptr); - allocator.root()->Free(ptr2); - - EXPECT_TRUE(bucket->empty_slot_spans_head); - EXPECT_TRUE(bucket->empty_slot_spans_head->next_slot_span); - EXPECT_EQ(0u, slot_span->num_allocated_slots); - EXPECT_EQ(0u, slot_span2->num_allocated_slots); - EXPECT_FALSE(slot_span->is_full()); - EXPECT_FALSE(slot_span->is_full()); - EXPECT_FALSE(slot_span->marked_full); - EXPECT_FALSE(slot_span2->marked_full); - EXPECT_TRUE(slot_span->get_freelist_head()); - EXPECT_TRUE(slot_span2->get_freelist_head()); - - CycleFreeCache(kTestAllocSize); - - EXPECT_FALSE(slot_span->get_freelist_head()); - EXPECT_FALSE(slot_span2->get_freelist_head()); - - EXPECT_TRUE(bucket->empty_slot_spans_head); - EXPECT_TRUE(bucket->empty_slot_spans_head->next_slot_span); - EXPECT_EQ(SlotSpanMetadata<internal::ThreadSafe>::get_sentinel_slot_span(), - bucket->active_slot_spans_head); - - // At this moment, we have two decommitted slot spans, on the empty list. - ptr = allocator.root()->Alloc(size, type_name); - EXPECT_TRUE(ptr); - allocator.root()->Free(ptr); - - EXPECT_EQ(SlotSpanMetadata<internal::ThreadSafe>::get_sentinel_slot_span(), - bucket->active_slot_spans_head); - EXPECT_TRUE(bucket->empty_slot_spans_head); - EXPECT_TRUE(bucket->decommitted_slot_spans_head); - - CycleFreeCache(kTestAllocSize); - - // We're now set up to trigger a historical bug by scanning over the active - // slot spans list. The current code gets into a different state, but we'll - // keep the test as being an interesting corner case. - ptr = allocator.root()->Alloc(size, type_name); - EXPECT_TRUE(ptr); - allocator.root()->Free(ptr); - - EXPECT_TRUE(bucket->is_valid()); - EXPECT_TRUE(bucket->empty_slot_spans_head); - EXPECT_TRUE(bucket->decommitted_slot_spans_head); -} - -#if defined(PA_HAS_DEATH_TESTS) - -// Unit tests that check if an allocation fails in "return null" mode, -// repeating it doesn't crash, and still returns null. The tests need to -// stress memory subsystem limits to do so, hence they try to allocate -// 6 GB of memory, each with a different per-allocation block sizes. -// -// On 64-bit systems we need to restrict the address space to force allocation -// failure, so these tests run only on POSIX systems that provide setrlimit(), -// and use it to limit address space to 6GB. -// -// Disable these tests on Android because, due to the allocation-heavy behavior, -// they tend to get OOM-killed rather than pass. -// -// Disable these test on Windows, since they run slower, so tend to timout and -// cause flake. -#if !BUILDFLAG(IS_WIN) && \ - (!defined(ARCH_CPU_64_BITS) || \ - (BUILDFLAG(IS_POSIX) && \ - !(BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_ANDROID)))) || \ - BUILDFLAG(IS_FUCHSIA) -#define MAYBE_RepeatedAllocReturnNullDirect RepeatedAllocReturnNullDirect -#define MAYBE_RepeatedReallocReturnNullDirect RepeatedReallocReturnNullDirect -#define MAYBE_RepeatedTryReallocReturnNullDirect \ - RepeatedTryReallocReturnNullDirect -#else -#define MAYBE_RepeatedAllocReturnNullDirect \ - DISABLED_RepeatedAllocReturnNullDirect -#define MAYBE_RepeatedReallocReturnNullDirect \ - DISABLED_RepeatedReallocReturnNullDirect -#define MAYBE_RepeatedTryReallocReturnNullDirect \ - DISABLED_RepeatedTryReallocReturnNullDirect -#endif - -// The following four tests wrap a called function in an expect death statement -// to perform their test, because they are non-hermetic. Specifically they are -// going to attempt to exhaust the allocatable memory, which leaves the -// allocator in a bad global state. -// Performing them as death tests causes them to be forked into their own -// process, so they won't pollute other tests. -// -// These tests are *very* slow when BUILDFLAG(PA_DCHECK_IS_ON), because they -// memset() many GiB of data (see crbug.com/1168168). -// TODO(lizeb): make these tests faster. -TEST_P(PartitionAllocDeathTest, MAYBE_RepeatedAllocReturnNullDirect) { - // A direct-mapped allocation size. - size_t direct_map_size = 32 * 1024 * 1024; - ASSERT_GT(direct_map_size, kMaxBucketed); - EXPECT_DEATH(DoReturnNullTest(direct_map_size, kPartitionAllocWithFlags), - "Passed DoReturnNullTest"); -} - -// Repeating above test with Realloc -TEST_P(PartitionAllocDeathTest, MAYBE_RepeatedReallocReturnNullDirect) { - size_t direct_map_size = 32 * 1024 * 1024; - ASSERT_GT(direct_map_size, kMaxBucketed); - EXPECT_DEATH(DoReturnNullTest(direct_map_size, kPartitionReallocWithFlags), - "Passed DoReturnNullTest"); -} - -// Repeating above test with TryRealloc -TEST_P(PartitionAllocDeathTest, MAYBE_RepeatedTryReallocReturnNullDirect) { - size_t direct_map_size = 32 * 1024 * 1024; - ASSERT_GT(direct_map_size, kMaxBucketed); - EXPECT_DEATH(DoReturnNullTest(direct_map_size, kPartitionRootTryRealloc), - "Passed DoReturnNullTest"); -} - -// TODO(crbug.com/1348221) re-enable the tests below, once the allocator -// actually returns nullptr for non direct-mapped allocations. -// When doing so, they will need to be made MAYBE_ like those above. -// -// Tests "return null" with a 512 kB block size. -TEST_P(PartitionAllocDeathTest, DISABLED_RepeatedAllocReturnNull) { - // A single-slot but non-direct-mapped allocation size. - size_t single_slot_size = 512 * 1024; - ASSERT_GT(single_slot_size, MaxRegularSlotSpanSize()); - ASSERT_LE(single_slot_size, kMaxBucketed); - EXPECT_DEATH(DoReturnNullTest(single_slot_size, kPartitionAllocWithFlags), - "Passed DoReturnNullTest"); -} - -// Repeating above test with Realloc. -TEST_P(PartitionAllocDeathTest, DISABLED_RepeatedReallocReturnNull) { - size_t single_slot_size = 512 * 1024; - ASSERT_GT(single_slot_size, MaxRegularSlotSpanSize()); - ASSERT_LE(single_slot_size, kMaxBucketed); - EXPECT_DEATH(DoReturnNullTest(single_slot_size, kPartitionReallocWithFlags), - "Passed DoReturnNullTest"); -} - -// Repeating above test with TryRealloc. -TEST_P(PartitionAllocDeathTest, DISABLED_RepeatedTryReallocReturnNull) { - size_t single_slot_size = 512 * 1024; - ASSERT_GT(single_slot_size, MaxRegularSlotSpanSize()); - ASSERT_LE(single_slot_size, kMaxBucketed); - EXPECT_DEATH(DoReturnNullTest(single_slot_size, kPartitionRootTryRealloc), - "Passed DoReturnNullTest"); -} - -#if PA_CONFIG(HAS_MEMORY_TAGGING) -// Check that Arm's memory tagging extension (MTE) is correctly protecting -// freed pointers. Writes to a free pointer should result in a crash. -TEST_P(PartitionAllocDeathTest, MTEProtectsFreedPtr) { - base::CPU cpu; - if (!cpu.has_mte()) { - // This test won't pass on systems without MTE. - GTEST_SKIP(); - } - - constexpr uint64_t kCookie = 0x1234567890ABCDEF; - constexpr uint64_t kQuarantined = 0xEFEFEFEFEFEFEFEF; - - // Make an arbitrary-sized small allocation. - size_t alloc_size = 64 - ExtraAllocSize(allocator); - uint64_t* ptr = - static_cast<uint64_t*>(allocator.root()->Alloc(alloc_size, type_name)); - EXPECT_TRUE(ptr); - - // Check that the allocation's writable. - *ptr = kCookie; - - // Invalidate ptr by freeing it. - allocator.root()->Free(ptr); - - // Writing to ptr after free() should crash - EXPECT_EXIT( - { - // Should be in synchronous MTE mode for running this test. - *ptr = kQuarantined; - }, - testing::KilledBySignal(SIGSEGV), ""); -} -#endif // PA_CONFIG(HAS_MEMORY_TAGGING) - -// Make sure that malloc(-1) dies. -// In the past, we had an integer overflow that would alias malloc(-1) to -// malloc(0), which is not good. -TEST_P(PartitionAllocDeathTest, LargeAllocs) { - // Largest alloc. - EXPECT_DEATH(allocator.root()->Alloc(static_cast<size_t>(-1), type_name), ""); - // And the smallest allocation we expect to die. - // TODO(bartekn): Separate into its own test, as it wouldn't run (same below). - EXPECT_DEATH(allocator.root()->Alloc(MaxDirectMapped() + 1, type_name), ""); -} - -// These tests don't work deterministically when BRP is enabled on certain -// architectures. On Free(), BRP's ref-count gets overwritten by an encoded -// freelist pointer. On little-endian 64-bit architectures, this happens to be -// always an even number, which will triggers BRP's own CHECK (sic!). On other -// architectures, it's likely to be an odd number >1, which will fool BRP into -// thinking the memory isn't freed and still referenced, thus making it -// quarantine it and return early, before PA_CHECK(slot_start != freelist_head) -// is reached. -// TODO(bartekn): Enable in the BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) case. -#if !BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) || \ - (BUILDFLAG(HAS_64_BIT_POINTERS) && defined(ARCH_CPU_LITTLE_ENDIAN)) - -// Check that our immediate double-free detection works. -TEST_P(PartitionAllocDeathTest, ImmediateDoubleFree) { - void* ptr = allocator.root()->Alloc(kTestAllocSize, type_name); - EXPECT_TRUE(ptr); - allocator.root()->Free(ptr); - EXPECT_DEATH(allocator.root()->Free(ptr), ""); -} - -// As above, but when this isn't the only slot in the span. -TEST_P(PartitionAllocDeathTest, ImmediateDoubleFree2ndSlot) { - void* ptr0 = allocator.root()->Alloc(kTestAllocSize, type_name); - EXPECT_TRUE(ptr0); - void* ptr = allocator.root()->Alloc(kTestAllocSize, type_name); - EXPECT_TRUE(ptr); - allocator.root()->Free(ptr); - EXPECT_DEATH(allocator.root()->Free(ptr), ""); - allocator.root()->Free(ptr0); -} - -// Check that our double-free detection based on |num_allocated_slots| not going -// below 0 works. -// -// Unlike in ImmediateDoubleFree test, we can't have a 2ndSlot version, as this -// protection wouldn't work when there is another slot present in the span. It -// will prevent |num_allocated_slots| from going below 0. -TEST_P(PartitionAllocDeathTest, NumAllocatedSlotsDoubleFree) { - void* ptr = allocator.root()->Alloc(kTestAllocSize, type_name); - EXPECT_TRUE(ptr); - void* ptr2 = allocator.root()->Alloc(kTestAllocSize, type_name); - EXPECT_TRUE(ptr2); - allocator.root()->Free(ptr); - allocator.root()->Free(ptr2); - // This is not an immediate double-free so our immediate detection won't - // fire. However, it does take |num_allocated_slots| to -1, which is illegal - // and should be trapped. - EXPECT_DEATH(allocator.root()->Free(ptr), ""); -} - -#endif // !BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) || \ - // (BUILDFLAG(HAS_64_BIT_POINTERS) && defined(ARCH_CPU_LITTLE_ENDIAN)) - -// Check that guard pages are present where expected. -TEST_P(PartitionAllocDeathTest, DirectMapGuardPages) { - const size_t kSizes[] = { - kMaxBucketed + ExtraAllocSize(allocator) + 1, - kMaxBucketed + SystemPageSize(), kMaxBucketed + PartitionPageSize(), - partition_alloc::internal::base::bits::AlignUp( - kMaxBucketed + kSuperPageSize, kSuperPageSize) - - PartitionRoot<ThreadSafe>::GetDirectMapMetadataAndGuardPagesSize()}; - for (size_t size : kSizes) { - ASSERT_GT(size, kMaxBucketed); - size -= ExtraAllocSize(allocator); - EXPECT_GT(size, kMaxBucketed) - << "allocation not large enough for direct allocation"; - void* ptr = allocator.root()->Alloc(size, type_name); - - EXPECT_TRUE(ptr); - char* char_ptr = static_cast<char*>(ptr) - kPointerOffset; - - EXPECT_DEATH(*(char_ptr - 1) = 'A', ""); - EXPECT_DEATH(*(char_ptr + partition_alloc::internal::base::bits::AlignUp( - size, SystemPageSize())) = 'A', - ""); - - allocator.root()->Free(ptr); - } -} - -// These tests rely on precise layout. They handle cookie, not ref-count. -#if !BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) && \ - PA_CONFIG(HAS_FREELIST_SHADOW_ENTRY) - -TEST_P(PartitionAllocDeathTest, UseAfterFreeDetection) { - base::CPU cpu; - void* data = allocator.root()->Alloc(100, ""); - allocator.root()->Free(data); - - // use after free, not crashing here, but the next allocation should crash, - // since we corrupted the freelist. - memset(data, 0x42, 100); - EXPECT_DEATH(allocator.root()->Alloc(100, ""), ""); -} - -TEST_P(PartitionAllocDeathTest, FreelistCorruption) { - base::CPU cpu; - const size_t alloc_size = 2 * sizeof(void*); - void** fake_freelist_entry = - static_cast<void**>(allocator.root()->Alloc(alloc_size, "")); - fake_freelist_entry[0] = nullptr; - fake_freelist_entry[1] = nullptr; - - void** uaf_data = - static_cast<void**>(allocator.root()->Alloc(alloc_size, "")); - allocator.root()->Free(uaf_data); - // Try to confuse the allocator. This is still easy to circumvent willingly, - // "just" need to set uaf_data[1] to ~uaf_data[0]. - void* previous_uaf_data = uaf_data[0]; - uaf_data[0] = fake_freelist_entry; - EXPECT_DEATH(allocator.root()->Alloc(alloc_size, ""), ""); - - // Restore the freelist entry value, otherwise freelist corruption is detected - // in TearDown(), crashing this process. - uaf_data[0] = previous_uaf_data; - - allocator.root()->Free(fake_freelist_entry); -} - -// With BUILDFLAG(PA_DCHECK_IS_ON), cookie already handles off-by-one detection. -#if !BUILDFLAG(PA_DCHECK_IS_ON) -TEST_P(PartitionAllocDeathTest, OffByOneDetection) { - base::CPU cpu; - const size_t alloc_size = 2 * sizeof(void*); - char* array = static_cast<char*>(allocator.root()->Alloc(alloc_size, "")); - if (cpu.has_mte()) { - EXPECT_DEATH(array[alloc_size] = 'A', ""); - } else { - char previous_value = array[alloc_size]; - // volatile is required to prevent the compiler from getting too clever and - // eliding the out-of-bounds write. The root cause is that the PA_MALLOC_FN - // annotation tells the compiler (among other things) that the returned - // value cannot alias anything. - *const_cast<volatile char*>(&array[alloc_size]) = 'A'; - // Crash at the next allocation. This assumes that we are touching a new, - // non-randomized slot span, where the next slot to be handed over to the - // application directly follows the current one. - EXPECT_DEATH(allocator.root()->Alloc(alloc_size, ""), ""); - - // Restore integrity, otherwise the process will crash in TearDown(). - array[alloc_size] = previous_value; - } -} - -TEST_P(PartitionAllocDeathTest, OffByOneDetectionWithRealisticData) { - base::CPU cpu; - const size_t alloc_size = 2 * sizeof(void*); - void** array = static_cast<void**>(allocator.root()->Alloc(alloc_size, "")); - char valid; - if (cpu.has_mte()) { - EXPECT_DEATH(array[2] = &valid, ""); - } else { - void* previous_value = array[2]; - // As above, needs volatile to convince the compiler to perform the write. - *const_cast<void* volatile*>(&array[2]) = &valid; - // Crash at the next allocation. This assumes that we are touching a new, - // non-randomized slot span, where the next slot to be handed over to the - // application directly follows the current one. - EXPECT_DEATH(allocator.root()->Alloc(alloc_size, ""), ""); - array[2] = previous_value; - } -} -#endif // !BUILDFLAG(PA_DCHECK_IS_ON) - -#endif // !BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) && - // PA_CONFIG(HAS_FREELIST_SHADOW_ENTRY) - -#endif // !defined(PA_HAS_DEATH_TESTS) - -// Tests that |PartitionDumpStats| and |PartitionDumpStats| run without -// crashing and return non-zero values when memory is allocated. -TEST_P(PartitionAllocTest, DumpMemoryStats) { - { - void* ptr = allocator.root()->Alloc(kTestAllocSize, type_name); - MockPartitionStatsDumper mock_stats_dumper; - allocator.root()->DumpStats("mock_allocator", false /* detailed dump */, - &mock_stats_dumper); - EXPECT_TRUE(mock_stats_dumper.IsMemoryAllocationRecorded()); - allocator.root()->Free(ptr); - } - - // This series of tests checks the active -> empty -> decommitted states. - { - { - void* ptr = - allocator.root()->Alloc(2048 - ExtraAllocSize(allocator), type_name); - MockPartitionStatsDumper dumper; - allocator.root()->DumpStats("mock_allocator", false /* detailed dump */, - &dumper); - EXPECT_TRUE(dumper.IsMemoryAllocationRecorded()); - - const PartitionBucketMemoryStats* stats = dumper.GetBucketStats(2048); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_EQ(2048u, stats->bucket_slot_size); - EXPECT_EQ(2048u, stats->active_bytes); - EXPECT_EQ(1u, stats->active_count); - EXPECT_EQ(SystemPageSize(), stats->resident_bytes); - EXPECT_EQ(0u, stats->decommittable_bytes); - EXPECT_EQ(0u, stats->discardable_bytes); - EXPECT_EQ(0u, stats->num_full_slot_spans); - EXPECT_EQ(1u, stats->num_active_slot_spans); - EXPECT_EQ(0u, stats->num_empty_slot_spans); - EXPECT_EQ(0u, stats->num_decommitted_slot_spans); - allocator.root()->Free(ptr); - } - - { - MockPartitionStatsDumper dumper; - allocator.root()->DumpStats("mock_allocator", false /* detailed dump */, - &dumper); - EXPECT_FALSE(dumper.IsMemoryAllocationRecorded()); - - const PartitionBucketMemoryStats* stats = dumper.GetBucketStats(2048); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_EQ(2048u, stats->bucket_slot_size); - EXPECT_EQ(0u, stats->active_bytes); - EXPECT_EQ(0u, stats->active_count); - EXPECT_EQ(SystemPageSize(), stats->resident_bytes); - EXPECT_EQ(SystemPageSize(), stats->decommittable_bytes); - EXPECT_EQ(0u, stats->discardable_bytes); - EXPECT_EQ(0u, stats->num_full_slot_spans); - EXPECT_EQ(0u, stats->num_active_slot_spans); - EXPECT_EQ(1u, stats->num_empty_slot_spans); - EXPECT_EQ(0u, stats->num_decommitted_slot_spans); - } - - // TODO(crbug.com/722911): Commenting this out causes this test to fail when - // run singly (--gtest_filter=PartitionAllocTest.DumpMemoryStats), but not - // when run with the others (--gtest_filter=PartitionAllocTest.*). - CycleFreeCache(kTestAllocSize); - - { - MockPartitionStatsDumper dumper; - allocator.root()->DumpStats("mock_allocator", false /* detailed dump */, - &dumper); - EXPECT_FALSE(dumper.IsMemoryAllocationRecorded()); - - const PartitionBucketMemoryStats* stats = dumper.GetBucketStats(2048); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_EQ(2048u, stats->bucket_slot_size); - EXPECT_EQ(0u, stats->active_bytes); - EXPECT_EQ(0u, stats->active_count); - EXPECT_EQ(0u, stats->resident_bytes); - EXPECT_EQ(0u, stats->decommittable_bytes); - EXPECT_EQ(0u, stats->discardable_bytes); - EXPECT_EQ(0u, stats->num_full_slot_spans); - EXPECT_EQ(0u, stats->num_active_slot_spans); - EXPECT_EQ(0u, stats->num_empty_slot_spans); - EXPECT_EQ(1u, stats->num_decommitted_slot_spans); - } - } - - // This test checks for correct empty slot span list accounting. - { - size_t size = PartitionPageSize() - ExtraAllocSize(allocator); - void* ptr1 = allocator.root()->Alloc(size, type_name); - void* ptr2 = allocator.root()->Alloc(size, type_name); - allocator.root()->Free(ptr1); - allocator.root()->Free(ptr2); - - CycleFreeCache(kTestAllocSize); - - ptr1 = allocator.root()->Alloc(size, type_name); - - { - MockPartitionStatsDumper dumper; - allocator.root()->DumpStats("mock_allocator", false /* detailed dump */, - &dumper); - EXPECT_TRUE(dumper.IsMemoryAllocationRecorded()); - - const PartitionBucketMemoryStats* stats = - dumper.GetBucketStats(PartitionPageSize()); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_EQ(PartitionPageSize(), stats->bucket_slot_size); - EXPECT_EQ(PartitionPageSize(), stats->active_bytes); - EXPECT_EQ(1u, stats->active_count); - EXPECT_EQ(PartitionPageSize(), stats->resident_bytes); - EXPECT_EQ(0u, stats->decommittable_bytes); - EXPECT_EQ(0u, stats->discardable_bytes); - EXPECT_EQ(1u, stats->num_full_slot_spans); - EXPECT_EQ(0u, stats->num_active_slot_spans); - EXPECT_EQ(0u, stats->num_empty_slot_spans); - EXPECT_EQ(1u, stats->num_decommitted_slot_spans); - } - allocator.root()->Free(ptr1); - } - - // This test checks for correct direct mapped accounting. - { - size_t size_smaller = kMaxBucketed + 1; - size_t size_bigger = (kMaxBucketed * 2) + 1; - size_t real_size_smaller = - (size_smaller + SystemPageOffsetMask()) & SystemPageBaseMask(); - size_t real_size_bigger = - (size_bigger + SystemPageOffsetMask()) & SystemPageBaseMask(); - void* ptr = allocator.root()->Alloc(size_smaller, type_name); - void* ptr2 = allocator.root()->Alloc(size_bigger, type_name); - - { - MockPartitionStatsDumper dumper; - allocator.root()->DumpStats("mock_allocator", false /* detailed dump */, - &dumper); - EXPECT_TRUE(dumper.IsMemoryAllocationRecorded()); - - const PartitionBucketMemoryStats* stats = - dumper.GetBucketStats(real_size_smaller); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_TRUE(stats->is_direct_map); - EXPECT_EQ(real_size_smaller, stats->bucket_slot_size); - EXPECT_EQ(real_size_smaller, stats->active_bytes); - EXPECT_EQ(1u, stats->active_count); - EXPECT_EQ(real_size_smaller, stats->resident_bytes); - EXPECT_EQ(0u, stats->decommittable_bytes); - EXPECT_EQ(0u, stats->discardable_bytes); - EXPECT_EQ(1u, stats->num_full_slot_spans); - EXPECT_EQ(0u, stats->num_active_slot_spans); - EXPECT_EQ(0u, stats->num_empty_slot_spans); - EXPECT_EQ(0u, stats->num_decommitted_slot_spans); - - stats = dumper.GetBucketStats(real_size_bigger); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_TRUE(stats->is_direct_map); - EXPECT_EQ(real_size_bigger, stats->bucket_slot_size); - EXPECT_EQ(real_size_bigger, stats->active_bytes); - EXPECT_EQ(1u, stats->active_count); - EXPECT_EQ(real_size_bigger, stats->resident_bytes); - EXPECT_EQ(0u, stats->decommittable_bytes); - EXPECT_EQ(0u, stats->discardable_bytes); - EXPECT_EQ(1u, stats->num_full_slot_spans); - EXPECT_EQ(0u, stats->num_active_slot_spans); - EXPECT_EQ(0u, stats->num_empty_slot_spans); - EXPECT_EQ(0u, stats->num_decommitted_slot_spans); - } - - allocator.root()->Free(ptr2); - allocator.root()->Free(ptr); - - // Whilst we're here, allocate again and free with different ordering to - // give a workout to our linked list code. - ptr = allocator.root()->Alloc(size_smaller, type_name); - ptr2 = allocator.root()->Alloc(size_bigger, type_name); - allocator.root()->Free(ptr); - allocator.root()->Free(ptr2); - } - - // This test checks large-but-not-quite-direct allocations. - { - const size_t requested_size = 16 * SystemPageSize(); - void* ptr = allocator.root()->Alloc(requested_size + 1, type_name); - - { - MockPartitionStatsDumper dumper; - allocator.root()->DumpStats("mock_allocator", false /* detailed dump */, - &dumper); - EXPECT_TRUE(dumper.IsMemoryAllocationRecorded()); - - size_t slot_size = SizeToBucketSize(requested_size + 1); - const PartitionBucketMemoryStats* stats = - dumper.GetBucketStats(slot_size); - ASSERT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_FALSE(stats->is_direct_map); - EXPECT_EQ(slot_size, stats->bucket_slot_size); - EXPECT_EQ(requested_size + 1 + ExtraAllocSize(allocator), - stats->active_bytes); - EXPECT_EQ(1u, stats->active_count); - EXPECT_EQ(slot_size, stats->resident_bytes); - EXPECT_EQ(0u, stats->decommittable_bytes); - EXPECT_EQ((slot_size - (requested_size + 1)) / SystemPageSize() * - SystemPageSize(), - stats->discardable_bytes); - EXPECT_EQ(1u, stats->num_full_slot_spans); - EXPECT_EQ(0u, stats->num_active_slot_spans); - EXPECT_EQ(0u, stats->num_empty_slot_spans); - EXPECT_EQ(0u, stats->num_decommitted_slot_spans); - } - - allocator.root()->Free(ptr); - - { - MockPartitionStatsDumper dumper; - allocator.root()->DumpStats("mock_allocator", false /* detailed dump */, - &dumper); - EXPECT_FALSE(dumper.IsMemoryAllocationRecorded()); - - size_t slot_size = SizeToBucketSize(requested_size + 1); - const PartitionBucketMemoryStats* stats = - dumper.GetBucketStats(slot_size); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_FALSE(stats->is_direct_map); - EXPECT_EQ(slot_size, stats->bucket_slot_size); - EXPECT_EQ(0u, stats->active_bytes); - EXPECT_EQ(0u, stats->active_count); - EXPECT_EQ(slot_size, stats->resident_bytes); - EXPECT_EQ(slot_size, stats->decommittable_bytes); - EXPECT_EQ(0u, stats->num_full_slot_spans); - EXPECT_EQ(0u, stats->num_active_slot_spans); - EXPECT_EQ(1u, stats->num_empty_slot_spans); - EXPECT_EQ(0u, stats->num_decommitted_slot_spans); - } - - void* ptr2 = allocator.root()->Alloc(requested_size + SystemPageSize() + 1, - type_name); - EXPECT_EQ(ptr, ptr2); - - { - MockPartitionStatsDumper dumper; - allocator.root()->DumpStats("mock_allocator", false /* detailed dump */, - &dumper); - EXPECT_TRUE(dumper.IsMemoryAllocationRecorded()); - - size_t slot_size = - SizeToBucketSize(requested_size + SystemPageSize() + 1); - const PartitionBucketMemoryStats* stats = - dumper.GetBucketStats(slot_size); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_FALSE(stats->is_direct_map); - EXPECT_EQ(slot_size, stats->bucket_slot_size); - EXPECT_EQ( - requested_size + SystemPageSize() + 1 + ExtraAllocSize(allocator), - stats->active_bytes); - EXPECT_EQ(1u, stats->active_count); - EXPECT_EQ(slot_size, stats->resident_bytes); - EXPECT_EQ(0u, stats->decommittable_bytes); - EXPECT_EQ((slot_size - (requested_size + SystemPageSize() + 1)) / - SystemPageSize() * SystemPageSize(), - stats->discardable_bytes); - EXPECT_EQ(1u, stats->num_full_slot_spans); - EXPECT_EQ(0u, stats->num_active_slot_spans); - EXPECT_EQ(0u, stats->num_empty_slot_spans); - EXPECT_EQ(0u, stats->num_decommitted_slot_spans); - } - - allocator.root()->Free(ptr2); - } -} - -// Tests the API to purge freeable memory. -TEST_P(PartitionAllocTest, Purge) { - char* ptr = static_cast<char*>( - allocator.root()->Alloc(2048 - ExtraAllocSize(allocator), type_name)); - allocator.root()->Free(ptr); - { - MockPartitionStatsDumper dumper; - allocator.root()->DumpStats("mock_allocator", false /* detailed dump */, - &dumper); - EXPECT_FALSE(dumper.IsMemoryAllocationRecorded()); - - const PartitionBucketMemoryStats* stats = dumper.GetBucketStats(2048); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_EQ(SystemPageSize(), stats->decommittable_bytes); - EXPECT_EQ(SystemPageSize(), stats->resident_bytes); - } - allocator.root()->PurgeMemory(PurgeFlags::kDecommitEmptySlotSpans); - { - MockPartitionStatsDumper dumper; - allocator.root()->DumpStats("mock_allocator", false /* detailed dump */, - &dumper); - EXPECT_FALSE(dumper.IsMemoryAllocationRecorded()); - - const PartitionBucketMemoryStats* stats = dumper.GetBucketStats(2048); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_EQ(0u, stats->decommittable_bytes); - EXPECT_EQ(0u, stats->resident_bytes); - } - // Calling purge again here is a good way of testing we didn't mess up the - // state of the free cache ring. - allocator.root()->PurgeMemory(PurgeFlags::kDecommitEmptySlotSpans); - - // A single-slot but non-direct-mapped allocation size. - size_t single_slot_size = 512 * 1024; - ASSERT_GT(single_slot_size, MaxRegularSlotSpanSize()); - ASSERT_LE(single_slot_size, kMaxBucketed); - char* big_ptr = - static_cast<char*>(allocator.root()->Alloc(single_slot_size, type_name)); - allocator.root()->Free(big_ptr); - allocator.root()->PurgeMemory(PurgeFlags::kDecommitEmptySlotSpans); - - CHECK_PAGE_IN_CORE(ptr - kPointerOffset, false); - CHECK_PAGE_IN_CORE(big_ptr - kPointerOffset, false); -} - -// Tests that we prefer to allocate into a non-empty partition page over an -// empty one. This is an important aspect of minimizing memory usage for some -// allocation sizes, particularly larger ones. -TEST_P(PartitionAllocTest, PreferActiveOverEmpty) { - size_t size = (SystemPageSize() * 2) - ExtraAllocSize(allocator); - // Allocate 3 full slot spans worth of 8192-byte allocations. - // Each slot span for this size is 16384 bytes, or 1 partition page and 2 - // slots. - void* ptr1 = allocator.root()->Alloc(size, type_name); - void* ptr2 = allocator.root()->Alloc(size, type_name); - void* ptr3 = allocator.root()->Alloc(size, type_name); - void* ptr4 = allocator.root()->Alloc(size, type_name); - void* ptr5 = allocator.root()->Alloc(size, type_name); - void* ptr6 = allocator.root()->Alloc(size, type_name); - - SlotSpanMetadata<internal::ThreadSafe>* slot_span1 = - SlotSpanMetadata<internal::ThreadSafe>::FromSlotStart( - allocator.root()->ObjectToSlotStart(ptr1)); - SlotSpanMetadata<internal::ThreadSafe>* slot_span2 = - SlotSpanMetadata<internal::ThreadSafe>::FromSlotStart( - allocator.root()->ObjectToSlotStart(ptr3)); - SlotSpanMetadata<internal::ThreadSafe>* slot_span3 = - SlotSpanMetadata<internal::ThreadSafe>::FromSlotStart( - allocator.root()->ObjectToSlotStart(ptr6)); - EXPECT_NE(slot_span1, slot_span2); - EXPECT_NE(slot_span2, slot_span3); - PartitionBucket<internal::ThreadSafe>* bucket = slot_span1->bucket; - EXPECT_EQ(slot_span3, bucket->active_slot_spans_head); - - // Free up the 2nd slot in each slot span. - // This leaves the active list containing 3 slot spans, each with 1 used and 1 - // free slot. The active slot span will be the one containing ptr1. - allocator.root()->Free(ptr6); - allocator.root()->Free(ptr4); - allocator.root()->Free(ptr2); - EXPECT_EQ(slot_span1, bucket->active_slot_spans_head); - - // Empty the middle slot span in the active list. - allocator.root()->Free(ptr3); - EXPECT_EQ(slot_span1, bucket->active_slot_spans_head); - - // Empty the first slot span in the active list -- also the current slot span. - allocator.root()->Free(ptr1); - - // A good choice here is to re-fill the third slot span since the first two - // are empty. We used to fail that. - void* ptr7 = allocator.root()->Alloc(size, type_name); - PA_EXPECT_PTR_EQ(ptr6, ptr7); - EXPECT_EQ(slot_span3, bucket->active_slot_spans_head); - - allocator.root()->Free(ptr5); - allocator.root()->Free(ptr7); -} - -// Tests the API to purge discardable memory. -TEST_P(PartitionAllocTest, PurgeDiscardableSecondPage) { - // Free the second of two 4096 byte allocations and then purge. - void* ptr1 = allocator.root()->Alloc( - SystemPageSize() - ExtraAllocSize(allocator), type_name); - char* ptr2 = static_cast<char*>(allocator.root()->Alloc( - SystemPageSize() - ExtraAllocSize(allocator), type_name)); - allocator.root()->Free(ptr2); - SlotSpanMetadata<internal::ThreadSafe>* slot_span = - SlotSpanMetadata<internal::ThreadSafe>::FromSlotStart( - allocator.root()->ObjectToSlotStart(ptr1)); - EXPECT_EQ(2u, slot_span->num_unprovisioned_slots); - { - MockPartitionStatsDumper dumper; - allocator.root()->DumpStats("mock_allocator", false /* detailed dump */, - &dumper); - EXPECT_TRUE(dumper.IsMemoryAllocationRecorded()); - - const PartitionBucketMemoryStats* stats = - dumper.GetBucketStats(SystemPageSize()); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_EQ(0u, stats->decommittable_bytes); - EXPECT_EQ(SystemPageSize(), stats->discardable_bytes); - EXPECT_EQ(SystemPageSize(), stats->active_bytes); - EXPECT_EQ(2 * SystemPageSize(), stats->resident_bytes); - } - CHECK_PAGE_IN_CORE(ptr2 - kPointerOffset, true); - allocator.root()->PurgeMemory(PurgeFlags::kDiscardUnusedSystemPages); - CHECK_PAGE_IN_CORE(ptr2 - kPointerOffset, false); - EXPECT_EQ(3u, slot_span->num_unprovisioned_slots); - - allocator.root()->Free(ptr1); -} - -TEST_P(PartitionAllocTest, PurgeDiscardableFirstPage) { - // Free the first of two 4096 byte allocations and then purge. - char* ptr1 = static_cast<char*>(allocator.root()->Alloc( - SystemPageSize() - ExtraAllocSize(allocator), type_name)); - void* ptr2 = allocator.root()->Alloc( - SystemPageSize() - ExtraAllocSize(allocator), type_name); - allocator.root()->Free(ptr1); - { - MockPartitionStatsDumper dumper; - allocator.root()->DumpStats("mock_allocator", false /* detailed dump */, - &dumper); - EXPECT_TRUE(dumper.IsMemoryAllocationRecorded()); - - const PartitionBucketMemoryStats* stats = - dumper.GetBucketStats(SystemPageSize()); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_EQ(0u, stats->decommittable_bytes); -#if BUILDFLAG(IS_WIN) - EXPECT_EQ(0u, stats->discardable_bytes); -#else - EXPECT_EQ(SystemPageSize(), stats->discardable_bytes); -#endif - EXPECT_EQ(SystemPageSize(), stats->active_bytes); - EXPECT_EQ(2 * SystemPageSize(), stats->resident_bytes); - } - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset, true); - allocator.root()->PurgeMemory(PurgeFlags::kDiscardUnusedSystemPages); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset, false); - - allocator.root()->Free(ptr2); -} - -TEST_P(PartitionAllocTest, PurgeDiscardableNonPageSizedAlloc) { - const size_t requested_size = 2.5 * SystemPageSize(); - char* ptr1 = static_cast<char*>(allocator.root()->Alloc( - requested_size - ExtraAllocSize(allocator), type_name)); - void* ptr2 = allocator.root()->Alloc( - requested_size - ExtraAllocSize(allocator), type_name); - void* ptr3 = allocator.root()->Alloc( - requested_size - ExtraAllocSize(allocator), type_name); - void* ptr4 = allocator.root()->Alloc( - requested_size - ExtraAllocSize(allocator), type_name); - memset(ptr1, 'A', requested_size - ExtraAllocSize(allocator)); - memset(ptr2, 'A', requested_size - ExtraAllocSize(allocator)); - allocator.root()->Free(ptr1); - allocator.root()->Free(ptr2); - { - MockPartitionStatsDumper dumper; - allocator.root()->DumpStats("mock_allocator", false /* detailed dump */, - &dumper); - EXPECT_TRUE(dumper.IsMemoryAllocationRecorded()); - - const PartitionBucketMemoryStats* stats = - dumper.GetBucketStats(requested_size); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_EQ(0u, stats->decommittable_bytes); -#if BUILDFLAG(IS_WIN) - EXPECT_EQ(3 * SystemPageSize(), stats->discardable_bytes); -#else - EXPECT_EQ(4 * SystemPageSize(), stats->discardable_bytes); -#endif - EXPECT_EQ(requested_size * 2, stats->active_bytes); - EXPECT_EQ(10 * SystemPageSize(), stats->resident_bytes); - } - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset, true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + SystemPageSize(), true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (SystemPageSize() * 2), true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (SystemPageSize() * 3), true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (SystemPageSize() * 4), true); - allocator.root()->PurgeMemory(PurgeFlags::kDiscardUnusedSystemPages); - // Except for Windows, the first page is discardable because the freelist - // pointer on this page is nullptr. Note that CHECK_PAGE_IN_CORE only executes - // checks for Linux and ChromeOS, not for Windows. - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset, false); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + SystemPageSize(), false); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (SystemPageSize() * 2), true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (SystemPageSize() * 3), false); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (SystemPageSize() * 4), false); - - allocator.root()->Free(ptr3); - allocator.root()->Free(ptr4); -} - -TEST_P(PartitionAllocTest, PurgeDiscardableNonPageSizedAllocOnSlotBoundary) { - const size_t requested_size = 2.5 * SystemPageSize(); - char* ptr1 = static_cast<char*>(allocator.root()->Alloc( - requested_size - ExtraAllocSize(allocator), type_name)); - void* ptr2 = allocator.root()->Alloc( - requested_size - ExtraAllocSize(allocator), type_name); - void* ptr3 = allocator.root()->Alloc( - requested_size - ExtraAllocSize(allocator), type_name); - void* ptr4 = allocator.root()->Alloc( - requested_size - ExtraAllocSize(allocator), type_name); - memset(ptr1, 'A', requested_size - ExtraAllocSize(allocator)); - memset(ptr2, 'A', requested_size - ExtraAllocSize(allocator)); - allocator.root()->Free(ptr2); - allocator.root()->Free(ptr1); - { - MockPartitionStatsDumper dumper; - allocator.root()->DumpStats("mock_allocator", false /* detailed dump */, - &dumper); - EXPECT_TRUE(dumper.IsMemoryAllocationRecorded()); - - const PartitionBucketMemoryStats* stats = - dumper.GetBucketStats(requested_size); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_EQ(0u, stats->decommittable_bytes); -#if BUILDFLAG(IS_WIN) - EXPECT_EQ(3 * SystemPageSize(), stats->discardable_bytes); -#else - EXPECT_EQ(4 * SystemPageSize(), stats->discardable_bytes); -#endif - EXPECT_EQ(requested_size * 2, stats->active_bytes); - EXPECT_EQ(10 * SystemPageSize(), stats->resident_bytes); - } - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset, true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + SystemPageSize(), true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (SystemPageSize() * 2), true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (SystemPageSize() * 3), true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (SystemPageSize() * 4), true); - allocator.root()->PurgeMemory(PurgeFlags::kDiscardUnusedSystemPages); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset, true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + SystemPageSize(), false); - // Except for Windows, the third page is discardable because the freelist - // pointer on this page is nullptr. Note that CHECK_PAGE_IN_CORE only executes - // checks for Linux and ChromeOS, not for Windows. - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (SystemPageSize() * 2), false); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (SystemPageSize() * 3), false); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (SystemPageSize() * 4), false); - - allocator.root()->Free(ptr3); - allocator.root()->Free(ptr4); -} - -TEST_P(PartitionAllocTest, PurgeDiscardableManyPages) { - // On systems with large pages, use less pages because: - // 1) There must be a bucket for kFirstAllocPages * SystemPageSize(), and - // 2) On low-end systems, using too many large pages can OOM during the test - const bool kHasLargePages = SystemPageSize() > 4096; - const size_t kFirstAllocPages = kHasLargePages ? 32 : 64; - const size_t kSecondAllocPages = kHasLargePages ? 31 : 61; - - // Detect case (1) from above. - PA_DCHECK(kFirstAllocPages * SystemPageSize() < (1UL << kMaxBucketedOrder)); - - const size_t kDeltaPages = kFirstAllocPages - kSecondAllocPages; - - { - ScopedPageAllocation p(allocator, kFirstAllocPages); - p.TouchAllPages(); - } - - ScopedPageAllocation p(allocator, kSecondAllocPages); - - MockPartitionStatsDumper dumper; - allocator.root()->DumpStats("mock_allocator", false /* detailed dump */, - &dumper); - EXPECT_TRUE(dumper.IsMemoryAllocationRecorded()); - - const PartitionBucketMemoryStats* stats = - dumper.GetBucketStats(kFirstAllocPages * SystemPageSize()); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_EQ(0u, stats->decommittable_bytes); - EXPECT_EQ(kDeltaPages * SystemPageSize(), stats->discardable_bytes); - EXPECT_EQ(kSecondAllocPages * SystemPageSize(), stats->active_bytes); - EXPECT_EQ(kFirstAllocPages * SystemPageSize(), stats->resident_bytes); - - for (size_t i = 0; i < kFirstAllocPages; i++) { - CHECK_PAGE_IN_CORE(p.PageAtIndex(i), true); - } - - allocator.root()->PurgeMemory(PurgeFlags::kDiscardUnusedSystemPages); - - for (size_t i = 0; i < kSecondAllocPages; i++) { - CHECK_PAGE_IN_CORE(p.PageAtIndex(i), true); - } - for (size_t i = kSecondAllocPages; i < kFirstAllocPages; i++) { - CHECK_PAGE_IN_CORE(p.PageAtIndex(i), false); - } -} - -TEST_P(PartitionAllocTest, PurgeDiscardableWithFreeListRewrite) { - // This sub-test tests truncation of the provisioned slots in a trickier - // case where the freelist is rewritten. - allocator.root()->PurgeMemory(PurgeFlags::kDecommitEmptySlotSpans); - char* ptr1 = static_cast<char*>(allocator.root()->Alloc( - SystemPageSize() - ExtraAllocSize(allocator), type_name)); - void* ptr2 = allocator.root()->Alloc( - SystemPageSize() - ExtraAllocSize(allocator), type_name); - void* ptr3 = allocator.root()->Alloc( - SystemPageSize() - ExtraAllocSize(allocator), type_name); - void* ptr4 = allocator.root()->Alloc( - SystemPageSize() - ExtraAllocSize(allocator), type_name); - ptr1[0] = 'A'; - ptr1[SystemPageSize()] = 'A'; - ptr1[SystemPageSize() * 2] = 'A'; - ptr1[SystemPageSize() * 3] = 'A'; - SlotSpanMetadata<internal::ThreadSafe>* slot_span = - SlotSpanMetadata<internal::ThreadSafe>::FromSlotStart( - allocator.root()->ObjectToSlotStart(ptr1)); - allocator.root()->Free(ptr2); - allocator.root()->Free(ptr4); - allocator.root()->Free(ptr1); - EXPECT_EQ(0u, slot_span->num_unprovisioned_slots); - - { - MockPartitionStatsDumper dumper; - allocator.root()->DumpStats("mock_allocator", false /* detailed dump */, - &dumper); - EXPECT_TRUE(dumper.IsMemoryAllocationRecorded()); - - const PartitionBucketMemoryStats* stats = - dumper.GetBucketStats(SystemPageSize()); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_EQ(0u, stats->decommittable_bytes); -#if BUILDFLAG(IS_WIN) - EXPECT_EQ(SystemPageSize(), stats->discardable_bytes); -#else - EXPECT_EQ(2 * SystemPageSize(), stats->discardable_bytes); -#endif - EXPECT_EQ(SystemPageSize(), stats->active_bytes); - EXPECT_EQ(4 * SystemPageSize(), stats->resident_bytes); - } - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset, true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + SystemPageSize(), true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (SystemPageSize() * 2), true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (SystemPageSize() * 3), true); - allocator.root()->PurgeMemory(PurgeFlags::kDiscardUnusedSystemPages); - EXPECT_EQ(1u, slot_span->num_unprovisioned_slots); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset, true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + SystemPageSize(), false); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (SystemPageSize() * 2), true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (SystemPageSize() * 3), false); - - // Let's check we didn't brick the freelist. - void* ptr1b = allocator.root()->Alloc( - SystemPageSize() - ExtraAllocSize(allocator), type_name); - PA_EXPECT_PTR_EQ(ptr1, ptr1b); - void* ptr2b = allocator.root()->Alloc( - SystemPageSize() - ExtraAllocSize(allocator), type_name); - PA_EXPECT_PTR_EQ(ptr2, ptr2b); - EXPECT_FALSE(slot_span->get_freelist_head()); - - allocator.root()->Free(ptr1); - allocator.root()->Free(ptr2); - allocator.root()->Free(ptr3); -} - -TEST_P(PartitionAllocTest, PurgeDiscardableDoubleTruncateFreeList) { - // This sub-test is similar, but tests a double-truncation. - allocator.root()->PurgeMemory(PurgeFlags::kDecommitEmptySlotSpans); - char* ptr1 = static_cast<char*>(allocator.root()->Alloc( - SystemPageSize() - ExtraAllocSize(allocator), type_name)); - void* ptr2 = allocator.root()->Alloc( - SystemPageSize() - ExtraAllocSize(allocator), type_name); - void* ptr3 = allocator.root()->Alloc( - SystemPageSize() - ExtraAllocSize(allocator), type_name); - void* ptr4 = allocator.root()->Alloc( - SystemPageSize() - ExtraAllocSize(allocator), type_name); - ptr1[0] = 'A'; - ptr1[SystemPageSize()] = 'A'; - ptr1[SystemPageSize() * 2] = 'A'; - ptr1[SystemPageSize() * 3] = 'A'; - SlotSpanMetadata<internal::ThreadSafe>* slot_span = - SlotSpanMetadata<internal::ThreadSafe>::FromSlotStart( - allocator.root()->ObjectToSlotStart(ptr1)); - allocator.root()->Free(ptr4); - allocator.root()->Free(ptr3); - EXPECT_EQ(0u, slot_span->num_unprovisioned_slots); - - { - MockPartitionStatsDumper dumper; - allocator.root()->DumpStats("mock_allocator", false /* detailed dump */, - &dumper); - EXPECT_TRUE(dumper.IsMemoryAllocationRecorded()); - - const PartitionBucketMemoryStats* stats = - dumper.GetBucketStats(SystemPageSize()); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_EQ(0u, stats->decommittable_bytes); - EXPECT_EQ(2 * SystemPageSize(), stats->discardable_bytes); - EXPECT_EQ(2 * SystemPageSize(), stats->active_bytes); - EXPECT_EQ(4 * SystemPageSize(), stats->resident_bytes); - } - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset, true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + SystemPageSize(), true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (SystemPageSize() * 2), true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (SystemPageSize() * 3), true); - allocator.root()->PurgeMemory(PurgeFlags::kDiscardUnusedSystemPages); - EXPECT_EQ(2u, slot_span->num_unprovisioned_slots); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset, true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + SystemPageSize(), true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (SystemPageSize() * 2), false); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (SystemPageSize() * 3), false); - - EXPECT_FALSE(slot_span->get_freelist_head()); - - allocator.root()->Free(ptr1); - allocator.root()->Free(ptr2); -} - -TEST_P(PartitionAllocTest, PurgeDiscardableSmallSlotsWithTruncate) { - size_t requested_size = 0.5 * SystemPageSize(); - char* ptr1 = static_cast<char*>(allocator.root()->Alloc( - requested_size - ExtraAllocSize(allocator), type_name)); - void* ptr2 = allocator.root()->Alloc( - requested_size - ExtraAllocSize(allocator), type_name); - void* ptr3 = allocator.root()->Alloc( - requested_size - ExtraAllocSize(allocator), type_name); - void* ptr4 = allocator.root()->Alloc( - requested_size - ExtraAllocSize(allocator), type_name); - allocator.root()->Free(ptr3); - allocator.root()->Free(ptr4); - SlotSpanMetadata<internal::ThreadSafe>* slot_span = - SlotSpanMetadata<internal::ThreadSafe>::FromSlotStart( - allocator.root()->ObjectToSlotStart(ptr1)); - EXPECT_EQ(4u, slot_span->num_unprovisioned_slots); - { - MockPartitionStatsDumper dumper; - allocator.root()->DumpStats("mock_allocator", false /* detailed dump */, - &dumper); - EXPECT_TRUE(dumper.IsMemoryAllocationRecorded()); - - const PartitionBucketMemoryStats* stats = - dumper.GetBucketStats(requested_size); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_EQ(0u, stats->decommittable_bytes); - EXPECT_EQ(SystemPageSize(), stats->discardable_bytes); - EXPECT_EQ(requested_size * 2, stats->active_bytes); - EXPECT_EQ(2 * SystemPageSize(), stats->resident_bytes); - } - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset, true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + SystemPageSize(), true); - allocator.root()->PurgeMemory(PurgeFlags::kDiscardUnusedSystemPages); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset, true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + SystemPageSize(), false); - EXPECT_EQ(6u, slot_span->num_unprovisioned_slots); - - allocator.root()->Free(ptr1); - allocator.root()->Free(ptr2); -} - -TEST_P(PartitionAllocTest, ActiveListMaintenance) { - size_t size = SystemPageSize() - ExtraAllocSize(allocator); - size_t real_size = size + ExtraAllocSize(allocator); - size_t bucket_index = - allocator.root()->SizeToBucketIndex(real_size, GetBucketDistribution()); - PartitionRoot<ThreadSafe>::Bucket* bucket = - &allocator.root()->buckets[bucket_index]; - ASSERT_EQ(bucket->slot_size, real_size); - size_t slots_per_span = bucket->num_system_pages_per_slot_span; - - // Make 10 full slot spans. - constexpr int kSpans = 10; - std::vector<std::vector<void*>> allocated_memory_spans(kSpans); - for (int span_index = 0; span_index < kSpans; span_index++) { - for (size_t i = 0; i < slots_per_span; i++) { - allocated_memory_spans[span_index].push_back( - allocator.root()->Alloc(size, "")); - } - } - - // Free one entry in the middle span, creating a partial slot span. - constexpr size_t kSpanIndex = 5; - allocator.root()->Free(allocated_memory_spans[kSpanIndex].back()); - allocated_memory_spans[kSpanIndex].pop_back(); - - // Empty the last slot span. - for (void* ptr : allocated_memory_spans[kSpans - 1]) { - allocator.root()->Free(ptr); - } - allocated_memory_spans.pop_back(); - - // The active list now is: - // Partial -> Empty -> Full -> Full -> ... -> Full - bucket->MaintainActiveList(); - - // Only one entry in the active list. - ASSERT_NE(bucket->active_slot_spans_head, - SlotSpanMetadata<ThreadSafe>::get_sentinel_slot_span()); - EXPECT_FALSE(bucket->active_slot_spans_head->next_slot_span); - - // The empty list has 1 entry. - ASSERT_NE(bucket->empty_slot_spans_head, - SlotSpanMetadata<ThreadSafe>::get_sentinel_slot_span()); - EXPECT_FALSE(bucket->empty_slot_spans_head->next_slot_span); - - // The rest are full slot spans. - EXPECT_EQ(8u, bucket->num_full_slot_spans); - - // Free all memory. - for (const auto& span : allocated_memory_spans) { - for (void* ptr : span) { - allocator.root()->Free(ptr); - } - } -} - -TEST_P(PartitionAllocTest, ReallocMovesCookie) { - // Resize so as to be sure to hit a "resize in place" case, and ensure that - // use of the entire result is compatible with the debug mode's cookie, even - // when the bucket size is large enough to span more than one partition page - // and we can track the "raw" size. See https://crbug.com/709271 - static const size_t kSize = MaxRegularSlotSpanSize(); - void* ptr = allocator.root()->Alloc(kSize + 1, type_name); - EXPECT_TRUE(ptr); - - memset(ptr, 0xbd, kSize + 1); - ptr = allocator.root()->Realloc(ptr, kSize + 2, type_name); - EXPECT_TRUE(ptr); - - memset(ptr, 0xbd, kSize + 2); - allocator.root()->Free(ptr); -} - -TEST_P(PartitionAllocTest, SmallReallocDoesNotMoveTrailingCookie) { - // For crbug.com/781473 - static constexpr size_t kSize = 264; - void* ptr = allocator.root()->Alloc(kSize, type_name); - EXPECT_TRUE(ptr); - - ptr = allocator.root()->Realloc(ptr, kSize + 16, type_name); - EXPECT_TRUE(ptr); - - allocator.root()->Free(ptr); -} - -TEST_P(PartitionAllocTest, ZeroFill) { - static constexpr size_t kAllZerosSentinel = - std::numeric_limits<size_t>::max(); - for (size_t size : kTestSizes) { - char* p = static_cast<char*>( - allocator.root()->AllocWithFlags(AllocFlags::kZeroFill, size, nullptr)); - size_t non_zero_position = kAllZerosSentinel; - for (size_t i = 0; i < size; ++i) { - if (0 != p[i]) { - non_zero_position = i; - break; - } - } - EXPECT_EQ(kAllZerosSentinel, non_zero_position) - << "test allocation size: " << size; - allocator.root()->Free(p); - } - - for (int i = 0; i < 10; ++i) { - SCOPED_TRACE(i); - AllocateRandomly(allocator.root(), 250, AllocFlags::kZeroFill); - } -} - -TEST_P(PartitionAllocTest, Bug_897585) { - // Need sizes big enough to be direct mapped and a delta small enough to - // allow re-use of the slot span when cookied. These numbers fall out of the - // test case in the indicated bug. - size_t kInitialSize = 983050; - size_t kDesiredSize = 983100; - ASSERT_GT(kInitialSize, kMaxBucketed); - ASSERT_GT(kDesiredSize, kMaxBucketed); - void* ptr = allocator.root()->AllocWithFlags(AllocFlags::kReturnNull, - kInitialSize, nullptr); - ASSERT_NE(nullptr, ptr); - ptr = allocator.root()->ReallocWithFlags(AllocFlags::kReturnNull, ptr, - kDesiredSize, nullptr); - ASSERT_NE(nullptr, ptr); - memset(ptr, 0xbd, kDesiredSize); - allocator.root()->Free(ptr); -} - -TEST_P(PartitionAllocTest, OverrideHooks) { - constexpr size_t kOverriddenSize = 1234; - constexpr const char* kOverriddenType = "Overridden type"; - constexpr unsigned char kOverriddenChar = 'A'; - - // Marked static so that we can use them in non-capturing lambdas below. - // (Non-capturing lambdas convert directly to function pointers.) - static volatile bool free_called = false; - static void* overridden_allocation = nullptr; - overridden_allocation = malloc(kOverriddenSize); - memset(overridden_allocation, kOverriddenChar, kOverriddenSize); - - PartitionAllocHooks::SetOverrideHooks( - [](void** out, unsigned int flags, size_t size, - const char* type_name) -> bool { - if (size == kOverriddenSize && type_name == kOverriddenType) { - *out = overridden_allocation; - return true; - } - return false; - }, - [](void* address) -> bool { - if (address == overridden_allocation) { - free_called = true; - return true; - } - return false; - }, - [](size_t* out, void* address) -> bool { - if (address == overridden_allocation) { - *out = kOverriddenSize; - return true; - } - return false; - }); - - void* ptr = allocator.root()->AllocWithFlags( - AllocFlags::kReturnNull, kOverriddenSize, kOverriddenType); - ASSERT_EQ(ptr, overridden_allocation); - - allocator.root()->Free(ptr); - EXPECT_TRUE(free_called); - - // overridden_allocation has not actually been freed so we can now immediately - // realloc it. - free_called = false; - ptr = allocator.root()->ReallocWithFlags(AllocFlags::kReturnNull, ptr, 1, - nullptr); - ASSERT_NE(ptr, nullptr); - EXPECT_NE(ptr, overridden_allocation); - EXPECT_TRUE(free_called); - EXPECT_EQ(*(char*)ptr, kOverriddenChar); - allocator.root()->Free(ptr); - - PartitionAllocHooks::SetOverrideHooks(nullptr, nullptr, nullptr); - free(overridden_allocation); -} - -TEST_P(PartitionAllocTest, Alignment) { - std::vector<void*> allocated_ptrs; - - for (size_t size = 1; size <= PartitionPageSize(); size <<= 1) { - if (size <= ExtraAllocSize(allocator)) { - continue; - } - size_t requested_size = size - ExtraAllocSize(allocator); - - // All allocations which are not direct-mapped occupy contiguous slots of a - // span, starting on a page boundary. This means that allocations are first - // rounded up to the nearest bucket size, then have an address of the form: - // (partition-page-aligned address) + i * bucket_size. - // - // All powers of two are bucket sizes, meaning that all power of two - // allocations smaller than a page will be aligned on the allocation size. - size_t expected_alignment = size; - for (int index = 0; index < 3; index++) { - void* ptr = allocator.root()->Alloc(requested_size, ""); - allocated_ptrs.push_back(ptr); - EXPECT_EQ(0u, - allocator.root()->ObjectToSlotStart(ptr) % expected_alignment) - << (index + 1) << "-th allocation of size=" << size; - } - } - - for (void* ptr : allocated_ptrs) { - allocator.root()->Free(ptr); - } -} - -TEST_P(PartitionAllocTest, FundamentalAlignment) { - // See the test above for details. Essentially, checking the bucket size is - // sufficient to ensure that alignment will always be respected, as long as - // the fundamental alignment is <= 16 bytes. - size_t fundamental_alignment = kAlignment; - for (size_t size = 0; size < SystemPageSize(); size++) { - // Allocate several pointers, as the first one in use in a size class will - // be aligned on a page boundary. - void* ptr = allocator.root()->Alloc(size, ""); - void* ptr2 = allocator.root()->Alloc(size, ""); - void* ptr3 = allocator.root()->Alloc(size, ""); - - EXPECT_EQ(UntagPtr(ptr) % fundamental_alignment, 0u); - EXPECT_EQ(UntagPtr(ptr2) % fundamental_alignment, 0u); - EXPECT_EQ(UntagPtr(ptr3) % fundamental_alignment, 0u); - - uintptr_t slot_start = allocator.root()->ObjectToSlotStart(ptr); -#if BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) - // The capacity(C) is slot size - ExtraAllocSize(allocator). - // Since slot size is multiples of kAlignment, - // C % kAlignment == (slot_size - ExtraAllocSize(allocator)) % kAlignment. - // C % kAlignment == (-ExtraAllocSize(allocator)) % kAlignment. - // Since kCookieSize is a multiple of kAlignment, - // C % kAlignment == (-kInSlotRefCountBufferSize) % kAlignment - // == (kAlignment - kInSlotRefCountBufferSize) % kAlignment. - EXPECT_EQ( - allocator.root()->AllocationCapacityFromSlotStart(slot_start) % - fundamental_alignment, - UseBRPPool() ? fundamental_alignment - kInSlotRefCountBufferSize : 0); -#else - EXPECT_EQ(allocator.root()->AllocationCapacityFromSlotStart(slot_start) % - fundamental_alignment, - -ExtraAllocSize(allocator) % fundamental_alignment); -#endif - - allocator.root()->Free(ptr); - allocator.root()->Free(ptr2); - allocator.root()->Free(ptr3); - } -} - -void VerifyAlignment(PartitionRoot<ThreadSafe>* root, - size_t size, - size_t alignment) { - std::vector<void*> allocated_ptrs; - - for (int index = 0; index < 3; index++) { - void* ptr = root->AlignedAllocWithFlags(0, alignment, size); - ASSERT_TRUE(ptr); - allocated_ptrs.push_back(ptr); - EXPECT_EQ(0ull, UntagPtr(ptr) % alignment) - << (index + 1) << "-th allocation of size=" << size - << ", alignment=" << alignment; - } - - for (void* ptr : allocated_ptrs) { - PartitionRoot<ThreadSafe>::Free(ptr); - } -} - -TEST_P(PartitionAllocTest, AlignedAllocations) { - size_t alloc_sizes[] = {1, - 10, - 100, - 1000, - 10000, - 60000, - 70000, - 130000, - 500000, - 900000, - kMaxBucketed + 1, - 2 * kMaxBucketed, - kSuperPageSize - 2 * PartitionPageSize(), - 4 * kMaxBucketed}; - for (size_t alloc_size : alloc_sizes) { - for (size_t alignment = 1; alignment <= kMaxSupportedAlignment; - alignment <<= 1) { - VerifyAlignment(aligned_allocator.root(), alloc_size, alignment); - - // Verify alignment on the regular allocator only when BRP is off, or when - // it's on in the "previous slot" mode. See the comment in SetUp(). -#if !BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) || \ - BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) - VerifyAlignment(allocator.root(), alloc_size, alignment); -#endif - } - } -} - -// Test that the optimized `GetSlotNumber` implementation produces valid -// results. -TEST_P(PartitionAllocTest, OptimizedGetSlotNumber) { - for (size_t i = 0; i < kNumBuckets; ++i) { - auto& bucket = allocator.root()->buckets[i]; - if (SizeToIndex(bucket.slot_size) != i) { - continue; - } - for (size_t slot = 0, offset = 0; slot < bucket.get_slots_per_span(); - ++slot, offset += bucket.slot_size) { - EXPECT_EQ(slot, bucket.GetSlotNumber(offset)); - EXPECT_EQ(slot, bucket.GetSlotNumber(offset + bucket.slot_size / 2)); - EXPECT_EQ(slot, bucket.GetSlotNumber(offset + bucket.slot_size - 1)); - } - } -} - -TEST_P(PartitionAllocTest, GetUsableSizeNull) { - EXPECT_EQ(0ULL, PartitionRoot<ThreadSafe>::GetUsableSize(nullptr)); -} - -TEST_P(PartitionAllocTest, GetUsableSize) { - size_t delta = SystemPageSize() + 1; - for (size_t size = 1; size <= kMinDirectMappedDownsize; size += delta) { - void* ptr = allocator.root()->Alloc(size, ""); - EXPECT_TRUE(ptr); - size_t usable_size = PartitionRoot<ThreadSafe>::GetUsableSize(ptr); - size_t usable_size_with_hack = - PartitionRoot<ThreadSafe>::GetUsableSizeWithMac11MallocSizeHack(ptr); -#if PA_CONFIG(ENABLE_MAC11_MALLOC_SIZE_HACK) - if (size != 32) -#endif - EXPECT_EQ(usable_size_with_hack, usable_size); - EXPECT_LE(size, usable_size); - memset(ptr, 0xDE, usable_size); - // Should not crash when free the ptr. - allocator.root()->Free(ptr); - } -} - -#if PA_CONFIG(ENABLE_MAC11_MALLOC_SIZE_HACK) -TEST_P(PartitionAllocTest, GetUsableSizeWithMac11MallocSizeHack) { - allocator.root()->EnableMac11MallocSizeHackForTesting(); - size_t size = internal::kMac11MallocSizeHackRequestedSize; - void* ptr = allocator.root()->Alloc(size, ""); - size_t usable_size = PartitionRoot<ThreadSafe>::GetUsableSize(ptr); - size_t usable_size_with_hack = - PartitionRoot<ThreadSafe>::GetUsableSizeWithMac11MallocSizeHack(ptr); - EXPECT_EQ(usable_size, internal::kMac11MallocSizeHackUsableSize); - EXPECT_EQ(usable_size_with_hack, size); - - allocator.root()->Free(ptr); -} -#endif // PA_CONFIG(ENABLE_MAC11_MALLOC_SIZE_HACK) - -TEST_P(PartitionAllocTest, Bookkeeping) { - auto& root = *allocator.root(); - - EXPECT_EQ(0U, root.total_size_of_committed_pages); - EXPECT_EQ(0U, root.max_size_of_committed_pages); - EXPECT_EQ(0U, root.get_total_size_of_allocated_bytes()); - EXPECT_EQ(0U, root.get_max_size_of_allocated_bytes()); - EXPECT_EQ(0U, root.total_size_of_super_pages); - size_t small_size = 1000; - - // A full slot span of size 1 partition page is committed. - void* ptr = root.Alloc(small_size - ExtraAllocSize(allocator), type_name); - // Lazy commit commits only needed pages. - size_t expected_committed_size = - kUseLazyCommit ? SystemPageSize() : PartitionPageSize(); - size_t expected_super_pages_size = kSuperPageSize; - size_t expected_max_committed_size = expected_committed_size; - size_t bucket_index = SizeToIndex(small_size - ExtraAllocSize(allocator)); - PartitionBucket<internal::ThreadSafe>* bucket = &root.buckets[bucket_index]; - size_t expected_total_allocated_size = bucket->slot_size; - size_t expected_max_allocated_size = expected_total_allocated_size; - - EXPECT_EQ(expected_committed_size, root.total_size_of_committed_pages); - EXPECT_EQ(expected_max_committed_size, root.max_size_of_committed_pages); - EXPECT_EQ(expected_total_allocated_size, - root.get_total_size_of_allocated_bytes()); - EXPECT_EQ(expected_max_allocated_size, - root.get_max_size_of_allocated_bytes()); - EXPECT_EQ(expected_super_pages_size, root.total_size_of_super_pages); - - // Freeing memory doesn't result in decommitting pages right away. - root.Free(ptr); - expected_total_allocated_size = 0U; - EXPECT_EQ(expected_committed_size, root.total_size_of_committed_pages); - EXPECT_EQ(expected_max_committed_size, root.max_size_of_committed_pages); - EXPECT_EQ(expected_total_allocated_size, - root.get_total_size_of_allocated_bytes()); - EXPECT_EQ(expected_max_allocated_size, - root.get_max_size_of_allocated_bytes()); - EXPECT_EQ(expected_super_pages_size, root.total_size_of_super_pages); - - // Allocating the same size lands it in the same slot span. - ptr = root.Alloc(small_size - ExtraAllocSize(allocator), type_name); - EXPECT_EQ(expected_committed_size, root.total_size_of_committed_pages); - EXPECT_EQ(expected_max_committed_size, root.max_size_of_committed_pages); - EXPECT_EQ(expected_max_allocated_size, - root.get_max_size_of_allocated_bytes()); - EXPECT_EQ(expected_super_pages_size, root.total_size_of_super_pages); - - // Freeing memory doesn't result in decommitting pages right away. - root.Free(ptr); - EXPECT_EQ(expected_committed_size, root.total_size_of_committed_pages); - EXPECT_EQ(expected_max_committed_size, root.max_size_of_committed_pages); - EXPECT_EQ(expected_max_allocated_size, - root.get_max_size_of_allocated_bytes()); - EXPECT_EQ(expected_super_pages_size, root.total_size_of_super_pages); - - // Allocating another size commits another slot span. - ptr = root.Alloc(2 * small_size - ExtraAllocSize(allocator), type_name); - expected_committed_size += - kUseLazyCommit ? SystemPageSize() : PartitionPageSize(); - expected_max_committed_size = - std::max(expected_max_committed_size, expected_committed_size); - expected_max_allocated_size = - std::max(expected_max_allocated_size, static_cast<size_t>(2048)); - EXPECT_EQ(expected_committed_size, root.total_size_of_committed_pages); - EXPECT_EQ(expected_max_committed_size, root.max_size_of_committed_pages); - EXPECT_EQ(expected_max_allocated_size, - root.get_max_size_of_allocated_bytes()); - EXPECT_EQ(expected_super_pages_size, root.total_size_of_super_pages); - - // Freeing memory doesn't result in decommitting pages right away. - root.Free(ptr); - EXPECT_EQ(expected_committed_size, root.total_size_of_committed_pages); - EXPECT_EQ(expected_max_committed_size, root.max_size_of_committed_pages); - EXPECT_EQ(expected_max_allocated_size, - root.get_max_size_of_allocated_bytes()); - EXPECT_EQ(expected_super_pages_size, root.total_size_of_super_pages); - - // Single-slot slot spans... - // - // When the system page size is larger than 4KiB, we don't necessarily have - // enough space in the superpage to store two of the largest bucketed - // allocations, particularly when we reserve extra space for e.g. bitmaps. - // To avoid this, we use something just below kMaxBucketed. - size_t big_size = kMaxBucketed * 4 / 5 - SystemPageSize(); - - ASSERT_GT(big_size, MaxRegularSlotSpanSize()); - ASSERT_LE(big_size, kMaxBucketed); - bucket_index = SizeToIndex(big_size - ExtraAllocSize(allocator)); - bucket = &root.buckets[bucket_index]; - // Assert the allocation doesn't fill the entire span nor entire partition - // page, to make the test more interesting. - ASSERT_LT(big_size, bucket->get_bytes_per_span()); - ASSERT_NE(big_size % PartitionPageSize(), 0U); - ptr = root.Alloc(big_size - ExtraAllocSize(allocator), type_name); - expected_committed_size += bucket->get_bytes_per_span(); - expected_max_committed_size = - std::max(expected_max_committed_size, expected_committed_size); - expected_total_allocated_size += bucket->get_bytes_per_span(); - expected_max_allocated_size = - std::max(expected_max_allocated_size, expected_total_allocated_size); - EXPECT_EQ(expected_committed_size, root.total_size_of_committed_pages); - EXPECT_EQ(expected_max_committed_size, root.max_size_of_committed_pages); - EXPECT_EQ(expected_total_allocated_size, - root.get_total_size_of_allocated_bytes()); - EXPECT_EQ(expected_max_allocated_size, - root.get_max_size_of_allocated_bytes()); - EXPECT_EQ(expected_super_pages_size, root.total_size_of_super_pages); - - // Allocating 2nd time doesn't overflow the super page... - void* ptr2 = root.Alloc(big_size - ExtraAllocSize(allocator), type_name); - expected_committed_size += bucket->get_bytes_per_span(); - expected_max_committed_size = - std::max(expected_max_committed_size, expected_committed_size); - expected_total_allocated_size += bucket->get_bytes_per_span(); - expected_max_allocated_size = - std::max(expected_max_allocated_size, expected_total_allocated_size); - EXPECT_EQ(expected_committed_size, root.total_size_of_committed_pages); - EXPECT_EQ(expected_max_committed_size, root.max_size_of_committed_pages); - EXPECT_EQ(expected_total_allocated_size, - root.get_total_size_of_allocated_bytes()); - EXPECT_EQ(expected_max_allocated_size, - root.get_max_size_of_allocated_bytes()); - EXPECT_EQ(expected_super_pages_size, root.total_size_of_super_pages); - - // ... but 3rd time does. - void* ptr3 = root.Alloc(big_size - ExtraAllocSize(allocator), type_name); - expected_committed_size += bucket->get_bytes_per_span(); - expected_max_committed_size = - std::max(expected_max_committed_size, expected_committed_size); - expected_total_allocated_size += bucket->get_bytes_per_span(); - expected_max_allocated_size = - std::max(expected_max_allocated_size, expected_total_allocated_size); - expected_super_pages_size += kSuperPageSize; - EXPECT_EQ(expected_committed_size, root.total_size_of_committed_pages); - EXPECT_EQ(expected_max_committed_size, root.max_size_of_committed_pages); - EXPECT_EQ(expected_total_allocated_size, - root.get_total_size_of_allocated_bytes()); - EXPECT_EQ(expected_max_allocated_size, - root.get_max_size_of_allocated_bytes()); - EXPECT_EQ(expected_super_pages_size, root.total_size_of_super_pages); - - // Freeing memory doesn't result in decommitting pages right away. - root.Free(ptr); - root.Free(ptr2); - root.Free(ptr3); - expected_total_allocated_size -= 3 * bucket->get_bytes_per_span(); - expected_max_allocated_size = - std::max(expected_max_allocated_size, expected_total_allocated_size); - EXPECT_EQ(expected_committed_size, root.total_size_of_committed_pages); - EXPECT_EQ(expected_max_committed_size, root.max_size_of_committed_pages); - EXPECT_EQ(expected_total_allocated_size, - root.get_total_size_of_allocated_bytes()); - EXPECT_EQ(expected_max_allocated_size, - root.get_max_size_of_allocated_bytes()); - EXPECT_EQ(expected_super_pages_size, root.total_size_of_super_pages); - - // Now everything should be decommitted. The reserved space for super pages - // stays the same and will never go away (by design). - root.PurgeMemory(PurgeFlags::kDecommitEmptySlotSpans); - expected_committed_size = 0; - EXPECT_EQ(expected_committed_size, root.total_size_of_committed_pages); - EXPECT_EQ(expected_max_committed_size, root.max_size_of_committed_pages); - EXPECT_EQ(expected_total_allocated_size, - root.get_total_size_of_allocated_bytes()); - EXPECT_EQ(expected_max_allocated_size, - root.get_max_size_of_allocated_bytes()); - EXPECT_EQ(expected_super_pages_size, root.total_size_of_super_pages); - - // None of the above should affect the direct map space. - EXPECT_EQ(0U, root.total_size_of_direct_mapped_pages); - - size_t huge_sizes[] = { - kMaxBucketed + SystemPageSize(), - kMaxBucketed + SystemPageSize() + 123, - kSuperPageSize - PageAllocationGranularity(), - kSuperPageSize - SystemPageSize() - PartitionPageSize(), - kSuperPageSize - PartitionPageSize(), - kSuperPageSize - SystemPageSize(), - kSuperPageSize, - kSuperPageSize + SystemPageSize(), - kSuperPageSize + PartitionPageSize(), - kSuperPageSize + SystemPageSize() + PartitionPageSize(), - kSuperPageSize + PageAllocationGranularity(), - kSuperPageSize + DirectMapAllocationGranularity(), - }; - size_t alignments[] = { - PartitionPageSize(), - 2 * PartitionPageSize(), - kMaxSupportedAlignment / 2, - kMaxSupportedAlignment, - }; - for (size_t huge_size : huge_sizes) { - ASSERT_GT(huge_size, kMaxBucketed); - for (size_t alignment : alignments) { - // For direct map, we commit only as many pages as needed. - size_t aligned_size = partition_alloc::internal::base::bits::AlignUp( - huge_size, SystemPageSize()); - ptr = root.AllocWithFlagsInternal( - 0, huge_size - ExtraAllocSize(allocator), alignment, type_name); - expected_committed_size += aligned_size; - expected_max_committed_size = - std::max(expected_max_committed_size, expected_committed_size); - expected_total_allocated_size += aligned_size; - expected_max_allocated_size = - std::max(expected_max_allocated_size, expected_total_allocated_size); - // The total reserved map includes metadata and guard pages at the ends. - // It also includes alignment. However, these would double count the first - // partition page, so it needs to be subtracted. - size_t surrounding_pages_size = - PartitionRoot<ThreadSafe>::GetDirectMapMetadataAndGuardPagesSize() + - alignment - PartitionPageSize(); - size_t expected_direct_map_size = - partition_alloc::internal::base::bits::AlignUp( - aligned_size + surrounding_pages_size, - DirectMapAllocationGranularity()); - EXPECT_EQ(expected_committed_size, root.total_size_of_committed_pages); - EXPECT_EQ(expected_max_committed_size, root.max_size_of_committed_pages); - EXPECT_EQ(expected_total_allocated_size, - root.get_total_size_of_allocated_bytes()); - EXPECT_EQ(expected_max_allocated_size, - root.get_max_size_of_allocated_bytes()); - EXPECT_EQ(expected_super_pages_size, root.total_size_of_super_pages); - EXPECT_EQ(expected_direct_map_size, - root.total_size_of_direct_mapped_pages); - - // Freeing memory in the diret map decommits pages right away. The address - // space is released for re-use too. - root.Free(ptr); - expected_committed_size -= aligned_size; - expected_direct_map_size = 0; - expected_max_committed_size = - std::max(expected_max_committed_size, expected_committed_size); - expected_total_allocated_size -= aligned_size; - expected_max_allocated_size = - std::max(expected_max_allocated_size, expected_total_allocated_size); - EXPECT_EQ(expected_committed_size, root.total_size_of_committed_pages); - EXPECT_EQ(expected_max_committed_size, root.max_size_of_committed_pages); - EXPECT_EQ(expected_total_allocated_size, - root.get_total_size_of_allocated_bytes()); - EXPECT_EQ(expected_max_allocated_size, - root.get_max_size_of_allocated_bytes()); - EXPECT_EQ(expected_super_pages_size, root.total_size_of_super_pages); - EXPECT_EQ(expected_direct_map_size, - root.total_size_of_direct_mapped_pages); - } - } -} - -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - -TEST_P(PartitionAllocTest, RefCountBasic) { - if (!UseBRPPool()) { - return; - } - - constexpr uint64_t kCookie = 0x1234567890ABCDEF; - constexpr uint64_t kQuarantined = 0xEFEFEFEFEFEFEFEF; - - size_t alloc_size = 64 - ExtraAllocSize(allocator); - uint64_t* ptr1 = - static_cast<uint64_t*>(allocator.root()->Alloc(alloc_size, type_name)); - EXPECT_TRUE(ptr1); - - *ptr1 = kCookie; - - auto* ref_count = - PartitionRefCountPointer(allocator.root()->ObjectToSlotStart(ptr1)); - EXPECT_TRUE(ref_count->IsAliveWithNoKnownRefs()); - - ref_count->Acquire(); - EXPECT_FALSE(ref_count->Release()); - EXPECT_TRUE(ref_count->IsAliveWithNoKnownRefs()); - EXPECT_EQ(*ptr1, kCookie); - - ref_count->Acquire(); - EXPECT_FALSE(ref_count->IsAliveWithNoKnownRefs()); - - allocator.root()->Free(ptr1); - // The allocation shouldn't be reclaimed, and its contents should be zapped. - // Retag ptr1 to get its correct MTE tag. - ptr1 = TagPtr(ptr1); - EXPECT_NE(*ptr1, kCookie); - EXPECT_EQ(*ptr1, kQuarantined); - - // The allocator should not reuse the original slot since its reference count - // doesn't equal zero. - uint64_t* ptr2 = - static_cast<uint64_t*>(allocator.root()->Alloc(alloc_size, type_name)); - EXPECT_NE(ptr1, ptr2); - allocator.root()->Free(ptr2); - - // When the last reference is released, the slot should become reusable. - // Retag ref_count because PartitionAlloc retags ptr to enforce quarantine. - ref_count = TagPtr(ref_count); - EXPECT_TRUE(ref_count->Release()); - PartitionAllocFreeForRefCounting(allocator.root()->ObjectToSlotStart(ptr1)); - uint64_t* ptr3 = - static_cast<uint64_t*>(allocator.root()->Alloc(alloc_size, type_name)); - EXPECT_EQ(ptr1, ptr3); - allocator.root()->Free(ptr3); -} - -void PartitionAllocTest::RunRefCountReallocSubtest(size_t orig_size, - size_t new_size) { - void* ptr1 = allocator.root()->Alloc(orig_size, type_name); - EXPECT_TRUE(ptr1); - - auto* ref_count1 = - PartitionRefCountPointer(allocator.root()->ObjectToSlotStart(ptr1)); - EXPECT_TRUE(ref_count1->IsAliveWithNoKnownRefs()); - - ref_count1->Acquire(); - EXPECT_FALSE(ref_count1->IsAliveWithNoKnownRefs()); - - void* ptr2 = allocator.root()->Realloc(ptr1, new_size, type_name); - EXPECT_TRUE(ptr2); - - // PartitionAlloc may retag memory areas on realloc (even if they - // do not move), so recover the true tag here. - ref_count1 = TagPtr(ref_count1); - - // Re-query ref-count. It may have moved if Realloc changed the slot. - auto* ref_count2 = - PartitionRefCountPointer(allocator.root()->ObjectToSlotStart(ptr2)); - - if (UntagPtr(ptr1) == UntagPtr(ptr2)) { - // If the slot didn't change, ref-count should stay the same. - EXPECT_EQ(ref_count1, ref_count2); - EXPECT_FALSE(ref_count2->IsAliveWithNoKnownRefs()); - - EXPECT_FALSE(ref_count2->Release()); - } else { - // If the allocation was moved to another slot, the old ref-count stayed - // in the same location in memory, is no longer alive, but still has a - // reference. The new ref-count is alive, but has no references. - EXPECT_NE(ref_count1, ref_count2); - EXPECT_FALSE(ref_count1->IsAlive()); - EXPECT_FALSE(ref_count1->IsAliveWithNoKnownRefs()); - EXPECT_TRUE(ref_count2->IsAliveWithNoKnownRefs()); - - EXPECT_TRUE(ref_count1->Release()); - PartitionAllocFreeForRefCounting(allocator.root()->ObjectToSlotStart(ptr1)); - } - - allocator.root()->Free(ptr2); -} - -TEST_P(PartitionAllocTest, RefCountRealloc) { - if (!UseBRPPool()) { - return; - } - - size_t alloc_sizes[] = {500, 5000, 50000, 400000}; - - for (size_t alloc_size : alloc_sizes) { - alloc_size -= ExtraAllocSize(allocator); - RunRefCountReallocSubtest(alloc_size, alloc_size - 9); - RunRefCountReallocSubtest(alloc_size, alloc_size + 9); - RunRefCountReallocSubtest(alloc_size, alloc_size * 2); - RunRefCountReallocSubtest(alloc_size, alloc_size / 2); - } -} - -int g_unretained_dangling_raw_ptr_detected_count = 0; - -class UnretainedDanglingRawPtrTest : public PartitionAllocTest { - public: - void SetUp() override { - PartitionAllocTest::SetUp(); - g_unretained_dangling_raw_ptr_detected_count = 0; - old_detected_fn_ = partition_alloc::GetUnretainedDanglingRawPtrDetectedFn(); - - partition_alloc::SetUnretainedDanglingRawPtrDetectedFn( - &UnretainedDanglingRawPtrTest::DanglingRawPtrDetected); - old_unretained_dangling_ptr_enabled_ = - partition_alloc::SetUnretainedDanglingRawPtrCheckEnabled(true); - } - void TearDown() override { - partition_alloc::SetUnretainedDanglingRawPtrDetectedFn(old_detected_fn_); - partition_alloc::SetUnretainedDanglingRawPtrCheckEnabled( - old_unretained_dangling_ptr_enabled_); - PartitionAllocTest::TearDown(); - } - - private: - static void DanglingRawPtrDetected(uintptr_t) { - g_unretained_dangling_raw_ptr_detected_count++; - } - - partition_alloc::DanglingRawPtrDetectedFn* old_detected_fn_; - bool old_unretained_dangling_ptr_enabled_; -}; - -INSTANTIATE_TEST_SUITE_P(AlternateBucketDistribution, - UnretainedDanglingRawPtrTest, - testing::ValuesIn(GetPartitionAllocTestParams())); - -TEST_P(UnretainedDanglingRawPtrTest, UnretainedDanglingPtrNoReport) { - if (!UseBRPPool()) { - return; - } - - void* ptr = allocator.root()->Alloc(kTestAllocSize, type_name); - EXPECT_TRUE(ptr); - auto* ref_count = - PartitionRefCountPointer(allocator.root()->ObjectToSlotStart(ptr)); - ref_count->Acquire(); - EXPECT_TRUE(ref_count->IsAlive()); - // Allocation is still live, so calling ReportIfDangling() should not result - // in any detections. - ref_count->ReportIfDangling(); - EXPECT_EQ(g_unretained_dangling_raw_ptr_detected_count, 0); - EXPECT_FALSE(ref_count->Release()); - allocator.root()->Free(ptr); -} - -TEST_P(UnretainedDanglingRawPtrTest, UnretainedDanglingPtrShouldReport) { - if (!UseBRPPool()) { - return; - } - - void* ptr = allocator.root()->Alloc(kTestAllocSize, type_name); - EXPECT_TRUE(ptr); - auto* ref_count = - PartitionRefCountPointer(allocator.root()->ObjectToSlotStart(ptr)); - ref_count->Acquire(); - EXPECT_TRUE(ref_count->IsAlive()); - allocator.root()->Free(ptr); - // At this point, memory shouldn't be alive... - EXPECT_FALSE(ref_count->IsAlive()); - // ...and we should report the ptr as dangling. - ref_count->ReportIfDangling(); - EXPECT_EQ(g_unretained_dangling_raw_ptr_detected_count, 1); - EXPECT_TRUE(ref_count->Release()); - - PartitionAllocFreeForRefCounting(allocator.root()->ObjectToSlotStart(ptr)); -} - -#if !BUILDFLAG(HAS_64_BIT_POINTERS) -TEST_P(PartitionAllocTest, BackupRefPtrGuardRegion) { - if (!UseBRPPool()) { - return; - } - - size_t alignment = internal::PageAllocationGranularity(); - - uintptr_t requested_address; - memset(&requested_address, internal::kQuarantinedByte, - sizeof(requested_address)); - requested_address = RoundDownToPageAllocationGranularity(requested_address); - - uintptr_t allocated_address = - AllocPages(requested_address, alignment, alignment, - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kReadWrite), - PageTag::kPartitionAlloc); - EXPECT_NE(allocated_address, requested_address); - - if (allocated_address) { - FreePages(allocated_address, alignment); - } -} -#endif // !BUILDFLAG(HAS_64_BIT_POINTERS) -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - -#if BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) - -// Allocate memory, and reference it from 3 raw_ptr. Among them 2 will be -// dangling. -TEST_P(PartitionAllocTest, DanglingPtr) { - if (!UseBRPPool()) { - return; - } - - CountDanglingRawPtr dangling_checks; - - // Allocate memory, and reference it from 3 raw_ptr. - uint64_t* ptr = static_cast<uint64_t*>( - allocator.root()->Alloc(64 - ExtraAllocSize(allocator), type_name)); - auto* ref_count = - PartitionRefCountPointer(allocator.root()->ObjectToSlotStart(ptr)); - ref_count->Acquire(); - ref_count->Acquire(); - ref_count->Acquire(); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - - // The first raw_ptr stops referencing it, before the memory has been - // released. - EXPECT_FALSE(ref_count->Release()); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - -#if BUILDFLAG(ENABLE_DANGLING_RAW_PTR_PERF_EXPERIMENT) - // Free it. This creates two dangling pointer. - allocator.root()->Free(ptr); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - - // The dangling raw_ptr stop referencing it. - EXPECT_FALSE(ref_count->Release()); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - - // The dangling raw_ptr stop referencing it again. - EXPECT_TRUE(ref_count->Release()); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); -#else - // Free it. This creates two dangling pointer. - allocator.root()->Free(ptr); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 1); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - - // The dangling raw_ptr stop referencing it. - EXPECT_FALSE(ref_count->Release()); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 1); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 1); - - // The dangling raw_ptr stop referencing it again. - EXPECT_TRUE(ref_count->Release()); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 1); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 2); -#endif - - PartitionAllocFreeForRefCounting(allocator.root()->ObjectToSlotStart(ptr)); -} - -// Allocate memory, and reference it from 3 -// raw_ptr<T, DisableDanglingPtrDetection>. Among them 2 will be dangling. This -// doesn't trigger any dangling raw_ptr checks. -TEST_P(PartitionAllocTest, DanglingDanglingPtr) { - if (!UseBRPPool()) { - return; - } - - CountDanglingRawPtr dangling_checks; - - // Allocate memory, and reference it from 3 raw_ptr. - uint64_t* ptr = static_cast<uint64_t*>( - allocator.root()->Alloc(64 - ExtraAllocSize(allocator), type_name)); - auto* ref_count = - PartitionRefCountPointer(allocator.root()->ObjectToSlotStart(ptr)); - ref_count->AcquireFromUnprotectedPtr(); - ref_count->AcquireFromUnprotectedPtr(); - ref_count->AcquireFromUnprotectedPtr(); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - - // The first raw_ptr<T, DisableDanglingPtrDetection> stops referencing it, - // before the memory has been released. - EXPECT_FALSE(ref_count->ReleaseFromUnprotectedPtr()); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - - // Free it. This creates two dangling raw_ptr<T, DisableDanglingPtrDetection>. - allocator.root()->Free(ptr); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - - // The dangling raw_ptr<T, DisableDanglingPtrDetection> stop referencing it. - EXPECT_FALSE(ref_count->ReleaseFromUnprotectedPtr()); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - - // The dangling raw_ptr<T, DisableDanglingPtrDetection> stop referencing it - // again. - EXPECT_TRUE(ref_count->ReleaseFromUnprotectedPtr()); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - - PartitionAllocFreeForRefCounting(allocator.root()->ObjectToSlotStart(ptr)); -} - -// When 'free' is called, it remain one raw_ptr<> and one -// raw_ptr<T, DisableDanglingPtrDetection>. The raw_ptr<> is released first. -TEST_P(PartitionAllocTest, DanglingMixedReleaseRawPtrFirst) { - if (!UseBRPPool()) { - return; - } - - CountDanglingRawPtr dangling_checks; - - uint64_t* ptr = static_cast<uint64_t*>( - allocator.root()->Alloc(64 - ExtraAllocSize(allocator), type_name)); - auto* ref_count = - PartitionRefCountPointer(allocator.root()->ObjectToSlotStart(ptr)); - // Acquire a raw_ptr<T, DisableDanglingPtrDetection> and a raw_ptr<>. - ref_count->AcquireFromUnprotectedPtr(); - ref_count->Acquire(); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - -#if BUILDFLAG(ENABLE_DANGLING_RAW_PTR_PERF_EXPERIMENT) - // Free it. - allocator.root()->Free(ptr); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - - // The raw_ptr<> stops referencing it. - EXPECT_FALSE(ref_count->Release()); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - - // The raw_ptr<T, DisableDanglingPtrDetection> stop referencing it. - EXPECT_TRUE(ref_count->ReleaseFromUnprotectedPtr()); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); -#else - // Free it. - allocator.root()->Free(ptr); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 1); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - - // The raw_ptr<> stops referencing it. - EXPECT_FALSE(ref_count->Release()); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 1); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 1); - - // The raw_ptr<T, DisableDanglingPtrDetection> stop referencing it. - EXPECT_TRUE(ref_count->ReleaseFromUnprotectedPtr()); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 1); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 1); -#endif - - PartitionAllocFreeForRefCounting(allocator.root()->ObjectToSlotStart(ptr)); -} - -// When 'free' is called, it remain one raw_ptr<> and one -// raw_ptr<T, DisableDanglingPtrDetection>. -// The raw_ptr<T, DisableDanglingPtrDetection> is released first. This -// triggers the dangling raw_ptr<> checks. -TEST_P(PartitionAllocTest, DanglingMixedReleaseDanglingPtrFirst) { - if (!UseBRPPool()) { - return; - } - - CountDanglingRawPtr dangling_checks; - - void* ptr = - allocator.root()->Alloc(64 - ExtraAllocSize(allocator), type_name); - auto* ref_count = - PartitionRefCountPointer(allocator.root()->ObjectToSlotStart(ptr)); - // Acquire a raw_ptr<T, DisableDanglingPtrDetection> and a raw_ptr<>. - ref_count->AcquireFromUnprotectedPtr(); - ref_count->Acquire(); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - -#if BUILDFLAG(ENABLE_DANGLING_RAW_PTR_PERF_EXPERIMENT) - // Free it. - allocator.root()->Free(ptr); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - - // The raw_ptr<> stops referencing it. - EXPECT_FALSE(ref_count->ReleaseFromUnprotectedPtr()); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - - // The raw_ptr<T, DisableDanglingPtrDetection> stop referencing it. - EXPECT_TRUE(ref_count->Release()); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); -#else - // Free it. - allocator.root()->Free(ptr); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 1); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - - // The raw_ptr<> stops referencing it. - EXPECT_FALSE(ref_count->ReleaseFromUnprotectedPtr()); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 1); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - - // The raw_ptr<T, DisableDanglingPtrDetection> stop referencing it. - EXPECT_TRUE(ref_count->Release()); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 1); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 1); -#endif - - PartitionAllocFreeForRefCounting(allocator.root()->ObjectToSlotStart(ptr)); -} - -// When 'free' is called, it remains one -// raw_ptr<T, DisableDanglingPtrDetection>, then it is used to acquire one -// dangling raw_ptr<>. Release the raw_ptr<> first. -TEST_P(PartitionAllocTest, DanglingPtrUsedToAcquireNewRawPtr) { - if (!UseBRPPool()) { - return; - } - - CountDanglingRawPtr dangling_checks; - - void* ptr = - allocator.root()->Alloc(64 - ExtraAllocSize(allocator), type_name); - auto* ref_count = - PartitionRefCountPointer(allocator.root()->ObjectToSlotStart(ptr)); - // Acquire a raw_ptr<T, DisableDanglingPtrDetection>. - ref_count->AcquireFromUnprotectedPtr(); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - - // Free it once. - allocator.root()->Free(ptr); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - - // A raw_ptr<> starts referencing it. - ref_count->Acquire(); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - - // The raw_ptr<> stops referencing it. - EXPECT_FALSE(ref_count->Release()); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - - // The raw_ptr<T, DisableDanglingPtrDetection> stops referencing it. - EXPECT_TRUE(ref_count->ReleaseFromUnprotectedPtr()); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - - PartitionAllocFreeForRefCounting(allocator.root()->ObjectToSlotStart(ptr)); -} - -// Same as 'DanglingPtrUsedToAcquireNewRawPtr', but release the -// raw_ptr<T, DisableDanglingPtrDetection> before the raw_ptr<>. -TEST_P(PartitionAllocTest, DanglingPtrUsedToAcquireNewRawPtrVariant) { - if (!UseBRPPool()) { - return; - } - - CountDanglingRawPtr dangling_checks; - - void* ptr = - allocator.root()->Alloc(64 - ExtraAllocSize(allocator), type_name); - auto* ref_count = - PartitionRefCountPointer(allocator.root()->ObjectToSlotStart(ptr)); - // Acquire a raw_ptr<T, DisableDanglingPtrDetection>. - ref_count->AcquireFromUnprotectedPtr(); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - - // Free it. - allocator.root()->Free(ptr); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - - // A raw_ptr<> starts referencing it. - ref_count->Acquire(); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - - // The raw_ptr<> stops referencing it. - EXPECT_FALSE(ref_count->ReleaseFromUnprotectedPtr()); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - - // The raw_ptr<T, DisableDanglingPtrDetection> stops referencing it. - EXPECT_TRUE(ref_count->Release()); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - - PartitionAllocFreeForRefCounting(allocator.root()->ObjectToSlotStart(ptr)); -} - -// Acquire a raw_ptr<T>, and release it before freeing memory. In the -// background, there is one raw_ptr<T, DisableDanglingPtrDetection>. This -// doesn't trigger any dangling raw_ptr<T> checks. -TEST_P(PartitionAllocTest, RawPtrReleasedBeforeFree) { - if (!UseBRPPool()) { - return; - } - - CountDanglingRawPtr dangling_checks; - - void* ptr = - allocator.root()->Alloc(64 - ExtraAllocSize(allocator), type_name); - auto* ref_count = - PartitionRefCountPointer(allocator.root()->ObjectToSlotStart(ptr)); - // Acquire a raw_ptr<T, DisableDanglingPtrDetection> and a raw_ptr<>. - ref_count->Acquire(); - ref_count->AcquireFromUnprotectedPtr(); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - - // Release the raw_ptr<>. - EXPECT_FALSE(ref_count->Release()); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - - // Free it. - allocator.root()->Free(ptr); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - - // The raw_ptr<T, DisableDanglingPtrDetection> stop referencing it. - EXPECT_TRUE(ref_count->ReleaseFromUnprotectedPtr()); - EXPECT_EQ(g_dangling_raw_ptr_detected_count, 0); - EXPECT_EQ(g_dangling_raw_ptr_released_count, 0); - - PartitionAllocFreeForRefCounting(allocator.root()->ObjectToSlotStart(ptr)); -} - -#if defined(PA_HAS_DEATH_TESTS) -// DCHECK message are stripped in official build. It causes death tests with -// matchers to fail. -#if !defined(OFFICIAL_BUILD) || !defined(NDEBUG) - -// Acquire() once, Release() twice => CRASH -TEST_P(PartitionAllocDeathTest, ReleaseUnderflowRawPtr) { - if (!UseBRPPool()) { - return; - } - - void* ptr = - allocator.root()->Alloc(64 - ExtraAllocSize(allocator), type_name); - auto* ref_count = - PartitionRefCountPointer(allocator.root()->ObjectToSlotStart(ptr)); - ref_count->Acquire(); - EXPECT_FALSE(ref_count->Release()); - EXPECT_DCHECK_DEATH(ref_count->Release()); - allocator.root()->Free(ptr); -} - -// AcquireFromUnprotectedPtr() once, ReleaseFromUnprotectedPtr() twice => CRASH -TEST_P(PartitionAllocDeathTest, ReleaseUnderflowDanglingPtr) { - if (!UseBRPPool()) { - return; - } - - void* ptr = - allocator.root()->Alloc(64 - ExtraAllocSize(allocator), type_name); - auto* ref_count = - PartitionRefCountPointer(allocator.root()->ObjectToSlotStart(ptr)); - ref_count->AcquireFromUnprotectedPtr(); - EXPECT_FALSE(ref_count->ReleaseFromUnprotectedPtr()); - EXPECT_DCHECK_DEATH(ref_count->ReleaseFromUnprotectedPtr()); - allocator.root()->Free(ptr); -} - -#endif //! defined(OFFICIAL_BUILD) || !defined(NDEBUG) -#endif // defined(PA_HAS_DEATH_TESTS) -#endif // BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) - -TEST_P(PartitionAllocTest, ReservationOffset) { - // For normal buckets, offset should be kOffsetTagNormalBuckets. - void* ptr = allocator.root()->Alloc(kTestAllocSize, type_name); - EXPECT_TRUE(ptr); - uintptr_t address = UntagPtr(ptr); - EXPECT_EQ(kOffsetTagNormalBuckets, *ReservationOffsetPointer(address)); - allocator.root()->Free(ptr); - - // For direct-map, - size_t large_size = kSuperPageSize * 5 + PartitionPageSize() * .5f; - ASSERT_GT(large_size, kMaxBucketed); - ptr = allocator.root()->Alloc(large_size, type_name); - EXPECT_TRUE(ptr); - address = UntagPtr(ptr); - EXPECT_EQ(0U, *ReservationOffsetPointer(address)); - EXPECT_EQ(1U, *ReservationOffsetPointer(address + kSuperPageSize)); - EXPECT_EQ(2U, *ReservationOffsetPointer(address + kSuperPageSize * 2)); - EXPECT_EQ(3U, *ReservationOffsetPointer(address + kSuperPageSize * 3)); - EXPECT_EQ(4U, *ReservationOffsetPointer(address + kSuperPageSize * 4)); - EXPECT_EQ(5U, *ReservationOffsetPointer(address + kSuperPageSize * 5)); - - // In-place realloc doesn't affect the offsets. - void* new_ptr = allocator.root()->Realloc(ptr, large_size * .8, type_name); - EXPECT_EQ(new_ptr, ptr); - EXPECT_EQ(0U, *ReservationOffsetPointer(address)); - EXPECT_EQ(1U, *ReservationOffsetPointer(address + kSuperPageSize)); - EXPECT_EQ(2U, *ReservationOffsetPointer(address + kSuperPageSize * 2)); - EXPECT_EQ(3U, *ReservationOffsetPointer(address + kSuperPageSize * 3)); - EXPECT_EQ(4U, *ReservationOffsetPointer(address + kSuperPageSize * 4)); - EXPECT_EQ(5U, *ReservationOffsetPointer(address + kSuperPageSize * 5)); - - allocator.root()->Free(ptr); - // After free, the offsets must be kOffsetTagNotAllocated. - EXPECT_EQ(kOffsetTagNotAllocated, *ReservationOffsetPointer(address)); - EXPECT_EQ(kOffsetTagNotAllocated, - *ReservationOffsetPointer(address + kSuperPageSize)); - EXPECT_EQ(kOffsetTagNotAllocated, - *ReservationOffsetPointer(address + kSuperPageSize * 2)); - EXPECT_EQ(kOffsetTagNotAllocated, - *ReservationOffsetPointer(address + kSuperPageSize * 3)); - EXPECT_EQ(kOffsetTagNotAllocated, - *ReservationOffsetPointer(address + kSuperPageSize * 4)); - EXPECT_EQ(kOffsetTagNotAllocated, - *ReservationOffsetPointer(address + kSuperPageSize * 5)); -} - -TEST_P(PartitionAllocTest, GetReservationStart) { - size_t large_size = kSuperPageSize * 3 + PartitionPageSize() * .5f; - ASSERT_GT(large_size, kMaxBucketed); - void* ptr = allocator.root()->Alloc(large_size, type_name); - EXPECT_TRUE(ptr); - uintptr_t slot_start = allocator.root()->ObjectToSlotStart(ptr); - uintptr_t reservation_start = slot_start - PartitionPageSize(); - EXPECT_EQ(0U, reservation_start & DirectMapAllocationGranularityOffsetMask()); - - uintptr_t address = UntagPtr(ptr); - for (uintptr_t a = address; a < address + large_size; ++a) { - uintptr_t address2 = GetDirectMapReservationStart(a) + PartitionPageSize(); - EXPECT_EQ(slot_start, address2); - } - - EXPECT_EQ(reservation_start, GetDirectMapReservationStart(slot_start)); - - allocator.root()->Free(ptr); -} - -TEST_P(PartitionAllocTest, CheckReservationType) { - void* ptr = allocator.root()->Alloc(kTestAllocSize, type_name); - EXPECT_TRUE(ptr); - uintptr_t address = UntagPtr(ptr); - uintptr_t address_to_check = address; - EXPECT_FALSE(IsReservationStart(address_to_check)); - EXPECT_TRUE(IsManagedByNormalBuckets(address_to_check)); - EXPECT_FALSE(IsManagedByDirectMap(address_to_check)); - EXPECT_TRUE(IsManagedByNormalBucketsOrDirectMap(address_to_check)); - address_to_check = address + kTestAllocSize - 1; - EXPECT_FALSE(IsReservationStart(address_to_check)); - EXPECT_TRUE(IsManagedByNormalBuckets(address_to_check)); - EXPECT_FALSE(IsManagedByDirectMap(address_to_check)); - EXPECT_TRUE(IsManagedByNormalBucketsOrDirectMap(address_to_check)); - address_to_check = - partition_alloc::internal::base::bits::AlignDown(address, kSuperPageSize); - EXPECT_TRUE(IsReservationStart(address_to_check)); - EXPECT_TRUE(IsManagedByNormalBuckets(address_to_check)); - EXPECT_FALSE(IsManagedByDirectMap(address_to_check)); - EXPECT_TRUE(IsManagedByNormalBucketsOrDirectMap(address_to_check)); - allocator.root()->Free(ptr); - // Freeing keeps a normal-bucket super page in memory. - address_to_check = - partition_alloc::internal::base::bits::AlignDown(address, kSuperPageSize); - EXPECT_TRUE(IsReservationStart(address_to_check)); - EXPECT_TRUE(IsManagedByNormalBuckets(address_to_check)); - EXPECT_FALSE(IsManagedByDirectMap(address_to_check)); - EXPECT_TRUE(IsManagedByNormalBucketsOrDirectMap(address_to_check)); - - size_t large_size = 2 * kSuperPageSize; - ASSERT_GT(large_size, kMaxBucketed); - ptr = allocator.root()->Alloc(large_size, type_name); - EXPECT_TRUE(ptr); - address = UntagPtr(ptr); - address_to_check = address; - EXPECT_FALSE(IsReservationStart(address_to_check)); - EXPECT_FALSE(IsManagedByNormalBuckets(address_to_check)); - EXPECT_TRUE(IsManagedByDirectMap(address_to_check)); - EXPECT_TRUE(IsManagedByNormalBucketsOrDirectMap(address_to_check)); - address_to_check = - partition_alloc::internal::base::bits::AlignUp(address, kSuperPageSize); - EXPECT_FALSE(IsReservationStart(address_to_check)); - EXPECT_FALSE(IsManagedByNormalBuckets(address_to_check)); - EXPECT_TRUE(IsManagedByDirectMap(address_to_check)); - EXPECT_TRUE(IsManagedByNormalBucketsOrDirectMap(address_to_check)); - address_to_check = address + large_size - 1; - EXPECT_FALSE(IsReservationStart(address_to_check)); - EXPECT_FALSE(IsManagedByNormalBuckets(address_to_check)); - EXPECT_TRUE(IsManagedByDirectMap(address_to_check)); - EXPECT_TRUE(IsManagedByNormalBucketsOrDirectMap(address_to_check)); - address_to_check = - partition_alloc::internal::base::bits::AlignDown(address, kSuperPageSize); - EXPECT_TRUE(IsReservationStart(address_to_check)); - EXPECT_FALSE(IsManagedByNormalBuckets(address_to_check)); - EXPECT_TRUE(IsManagedByDirectMap(address_to_check)); - EXPECT_TRUE(IsManagedByNormalBucketsOrDirectMap(address_to_check)); - allocator.root()->Free(ptr); - // Freeing releases direct-map super pages. - address_to_check = - partition_alloc::internal::base::bits::AlignDown(address, kSuperPageSize); -#if BUILDFLAG(PA_DCHECK_IS_ON) - // Expect to DCHECK on unallocated region. - EXPECT_DEATH_IF_SUPPORTED(IsReservationStart(address_to_check), ""); -#endif - EXPECT_FALSE(IsManagedByNormalBuckets(address_to_check)); - EXPECT_FALSE(IsManagedByDirectMap(address_to_check)); - EXPECT_FALSE(IsManagedByNormalBucketsOrDirectMap(address_to_check)); -} - -// Test for crash http://crbug.com/1169003. -TEST_P(PartitionAllocTest, CrossPartitionRootRealloc) { - // Size is large enough to satisfy it from a single-slot slot span - size_t test_size = MaxRegularSlotSpanSize() - ExtraAllocSize(allocator); - void* ptr = allocator.root()->AllocWithFlags(AllocFlags::kReturnNull, - test_size, nullptr); - EXPECT_TRUE(ptr); - - // Create new root and call PurgeMemory to simulate ConfigurePartitions(). - allocator.root()->PurgeMemory(PurgeFlags::kDecommitEmptySlotSpans | - PurgeFlags::kDiscardUnusedSystemPages); - auto* new_root = new PartitionRoot<ThreadSafe>({ - PartitionOptions::AlignedAlloc::kDisallowed, - PartitionOptions::ThreadCache::kDisabled, - PartitionOptions::Quarantine::kDisallowed, - PartitionOptions::Cookie::kAllowed, - PartitionOptions::BackupRefPtr::kDisabled, - PartitionOptions::BackupRefPtrZapping::kDisabled, - PartitionOptions::UseConfigurablePool::kNo, - }); - SetDistributionForPartitionRoot(new_root, GetBucketDistribution()); - - // Realloc from |allocator.root()| into |new_root|. - void* ptr2 = new_root->ReallocWithFlags(AllocFlags::kReturnNull, ptr, - test_size + 1024, nullptr); - EXPECT_TRUE(ptr2); - PA_EXPECT_PTR_NE(ptr, ptr2); -} - -TEST_P(PartitionAllocTest, FastPathOrReturnNull) { - size_t allocation_size = 64; - // The very first allocation is never a fast path one, since it needs a new - // super page and a new partition page. - EXPECT_FALSE(allocator.root()->AllocWithFlags( - AllocFlags::kFastPathOrReturnNull, allocation_size, "")); - void* ptr = allocator.root()->AllocWithFlags(0, allocation_size, ""); - ASSERT_TRUE(ptr); - - // Next one is, since the partition page has been activated. - void* ptr2 = allocator.root()->AllocWithFlags( - AllocFlags::kFastPathOrReturnNull, allocation_size, ""); - EXPECT_TRUE(ptr2); - - // First allocation of a different bucket is slow. - EXPECT_FALSE(allocator.root()->AllocWithFlags( - AllocFlags::kFastPathOrReturnNull, 2 * allocation_size, "")); - - size_t allocated_size = 2 * allocation_size; - std::vector<void*> ptrs; - while (void* new_ptr = allocator.root()->AllocWithFlags( - AllocFlags::kFastPathOrReturnNull, allocation_size, "")) { - ptrs.push_back(new_ptr); - allocated_size += allocation_size; - } - EXPECT_LE(allocated_size, - PartitionPageSize() * kMaxPartitionPagesPerRegularSlotSpan); - - for (void* ptr_to_free : ptrs) { - allocator.root()->FreeNoHooks(ptr_to_free); - } - - allocator.root()->FreeNoHooks(ptr); - allocator.root()->FreeNoHooks(ptr2); -} - -#if defined(PA_HAS_DEATH_TESTS) -// DCHECK message are stripped in official build. It causes death tests with -// matchers to fail. -#if !defined(OFFICIAL_BUILD) || !defined(NDEBUG) - -TEST_P(PartitionAllocDeathTest, CheckTriggered) { - EXPECT_DCHECK_DEATH_WITH(PA_CHECK(5 == 7), "Check failed.*5 == 7"); - EXPECT_DEATH(PA_CHECK(5 == 7), "Check failed.*5 == 7"); -} - -#endif // !defined(OFFICIAL_BUILD) && !defined(NDEBUG) -#endif // defined(PA_HAS_DEATH_TESTS) - -// Not on chromecast, since gtest considers extra output from itself as a test -// failure: -// https://ci.chromium.org/ui/p/chromium/builders/ci/Cast%20Audio%20Linux/98492/overview -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && defined(PA_HAS_DEATH_TESTS) && \ - !BUILDFLAG(PA_IS_CASTOS) - -namespace { - -PA_NOINLINE void FreeForTest(void* data) { - free(data); -} - -class ThreadDelegateForPreforkHandler - : public base::PlatformThreadForTesting::Delegate { - public: - ThreadDelegateForPreforkHandler(std::atomic<bool>& please_stop, - std::atomic<int>& started_threads, - const int alloc_size) - : please_stop_(please_stop), - started_threads_(started_threads), - alloc_size_(alloc_size) {} - - void ThreadMain() override { - started_threads_++; - while (!please_stop_.load(std::memory_order_relaxed)) { - void* ptr = malloc(alloc_size_); - - // A simple malloc() / free() pair can be discarded by the compiler (and - // is), making the test fail. It is sufficient to make |FreeForTest()| a - // PA_NOINLINE function for the call to not be eliminated, but it is - // required. - FreeForTest(ptr); - } - } - - private: - std::atomic<bool>& please_stop_; - std::atomic<int>& started_threads_; - const int alloc_size_; -}; - -} // namespace - -// Disabled because executing it causes Gtest to show a warning in the output, -// which confuses the runner on some platforms, making the test report an -// "UNKNOWN" status even though it succeeded. -TEST_P(PartitionAllocTest, DISABLED_PreforkHandler) { - std::atomic<bool> please_stop; - std::atomic<int> started_threads{0}; - - // Continuously allocates / frees memory, bypassing the thread cache. This - // makes it likely that this thread will own the lock, and that the - // EXPECT_EXIT() part will deadlock. - constexpr size_t kAllocSize = ThreadCache::kLargeSizeThreshold + 1; - ThreadDelegateForPreforkHandler delegate(please_stop, started_threads, - kAllocSize); - - constexpr int kThreads = 4; - base::PlatformThreadHandle thread_handles[kThreads]; - for (auto& thread_handle : thread_handles) { - base::PlatformThreadForTesting::Create(0, &delegate, &thread_handle); - } - // Make sure all threads are actually already running. - while (started_threads != kThreads) { - } - - EXPECT_EXIT( - { - void* ptr = malloc(kAllocSize); - FreeForTest(ptr); - exit(1); - }, - ::testing::ExitedWithCode(1), ""); - - please_stop.store(true); - for (auto& thread_handle : thread_handles) { - base::PlatformThreadForTesting::Join(thread_handle); - } -} - -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && - // PA_CONFIG(HAS_DEATH_TESTS) && !BUILDFLAG(PA_IS_CASTOS) - -// Checks the bucket index logic. -TEST_P(PartitionAllocTest, GetIndex) { - BucketIndexLookup lookup{}; - - for (size_t size = 0; size < kMaxBucketed; size++) { - size_t index = BucketIndexLookup::GetIndex(size); - ASSERT_GE(lookup.bucket_sizes()[index], size); - } - - // Make sure that power-of-two have exactly matching buckets. - for (size_t size = (1 << (kMinBucketedOrder - 1)); size < kMaxBucketed; - size <<= 1) { - size_t index = BucketIndexLookup::GetIndex(size); - ASSERT_EQ(lookup.bucket_sizes()[index], size); - } -} - -// Used to check alignment. If the compiler understands the annotations, the -// zeroing in the constructor uses aligned SIMD instructions. -TEST_P(PartitionAllocTest, MallocFunctionAnnotations) { - struct TestStruct { - uint64_t a = 0; - uint64_t b = 0; - }; - - void* buffer = Alloc(sizeof(TestStruct)); - // Should use "mov*a*ps" on x86_64. - auto* x = new (buffer) TestStruct(); - - EXPECT_EQ(x->a, 0u); - Free(buffer); -} - -// Test that the ConfigurablePool works properly. -TEST_P(PartitionAllocTest, ConfigurablePool) { - EXPECT_FALSE(IsConfigurablePoolAvailable()); - - // The rest is only applicable to 64-bit mode -#if defined(ARCH_CPU_64_BITS) - // Repeat the test for every possible Pool size - const size_t max_pool_size = PartitionAddressSpace::ConfigurablePoolMaxSize(); - const size_t min_pool_size = PartitionAddressSpace::ConfigurablePoolMinSize(); - for (size_t pool_size = max_pool_size; pool_size >= min_pool_size; - pool_size /= 2) { - PA_DCHECK(partition_alloc::internal::base::bits::IsPowerOfTwo(pool_size)); - EXPECT_FALSE(IsConfigurablePoolAvailable()); - uintptr_t pool_base = - AllocPages(pool_size, pool_size, - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kInaccessible), - PageTag::kPartitionAlloc); - EXPECT_NE(0u, pool_base); - PartitionAddressSpace::InitConfigurablePool(pool_base, pool_size); - - EXPECT_TRUE(IsConfigurablePoolAvailable()); - - auto* root = new PartitionRoot<ThreadSafe>({ - PartitionOptions::AlignedAlloc::kDisallowed, - PartitionOptions::ThreadCache::kDisabled, - PartitionOptions::Quarantine::kDisallowed, - PartitionOptions::Cookie::kAllowed, - PartitionOptions::BackupRefPtr::kDisabled, - PartitionOptions::BackupRefPtrZapping::kDisabled, - PartitionOptions::UseConfigurablePool::kIfAvailable, - }); - root->UncapEmptySlotSpanMemoryForTesting(); - SetDistributionForPartitionRoot(root, GetBucketDistribution()); - - const size_t count = 250; - std::vector<void*> allocations(count, nullptr); - for (size_t i = 0; i < count; ++i) { - const size_t size = kTestSizes[base::RandGenerator(kTestSizesCount)]; - allocations[i] = root->Alloc(size, nullptr); - EXPECT_NE(nullptr, allocations[i]); - // We don't Untag allocations here because MTE is disabled for - // configurable pools used by V8. - // https://bugs.chromium.org/p/v8/issues/detail?id=13117 - uintptr_t allocation_base = reinterpret_cast<uintptr_t>(allocations[i]); - EXPECT_EQ(allocation_base, UntagPtr(allocations[i])); - EXPECT_TRUE(allocation_base >= pool_base && - allocation_base < pool_base + pool_size); - } - - PartitionAddressSpace::UninitConfigurablePoolForTesting(); - FreePages(pool_base, pool_size); - } - -#endif // defined(ARCH_CPU_64_BITS) -} - -TEST_P(PartitionAllocTest, EmptySlotSpanSizeIsCapped) { - // Use another root, since the ones from the test harness disable the empty - // slot span size cap. - PartitionRoot<ThreadSafe> root; - root.Init({ - PartitionOptions::AlignedAlloc::kDisallowed, - PartitionOptions::ThreadCache::kDisabled, - PartitionOptions::Quarantine::kDisallowed, - PartitionOptions::Cookie::kAllowed, - PartitionOptions::BackupRefPtr::kDisabled, - PartitionOptions::BackupRefPtrZapping::kDisabled, - PartitionOptions::UseConfigurablePool::kNo, - }); - SetDistributionForPartitionRoot(&root, GetBucketDistribution()); - - // Allocate some memory, don't free it to keep committed memory. - std::vector<void*> allocated_memory; - const size_t size = SystemPageSize(); - const size_t count = 400; - for (size_t i = 0; i < count; i++) { - void* ptr = root.Alloc(size, ""); - allocated_memory.push_back(ptr); - } - ASSERT_GE(root.total_size_of_committed_pages.load(std::memory_order_relaxed), - size * count); - - // To create empty slot spans, allocate from single-slot slot spans, 128kiB at - // a time. - std::vector<void*> single_slot_allocated_memory; - constexpr size_t single_slot_count = kDefaultEmptySlotSpanRingSize - 1; - const size_t single_slot_size = MaxRegularSlotSpanSize() + 1; - // Make sure that even with allocation size rounding up, a single allocation - // is still below the threshold. - ASSERT_LT(MaxRegularSlotSpanSize() * 2, - ((count * size) >> root.max_empty_slot_spans_dirty_bytes_shift)); - for (size_t i = 0; i < single_slot_count; i++) { - void* ptr = root.Alloc(single_slot_size, ""); - single_slot_allocated_memory.push_back(ptr); - } - - // Free everything at once, creating as many empty slot spans as there are - // allocations (since they are from single-slot slot spans). - for (void* ptr : single_slot_allocated_memory) { - root.Free(ptr); - } - - // Still have some committed empty slot spans. - // PA_TS_UNCHECKED_READ() is not an issue here, since everything is - // single-threaded. - EXPECT_GT(PA_TS_UNCHECKED_READ(root.empty_slot_spans_dirty_bytes), 0u); - // But not all, as the cap triggered. - EXPECT_LT(PA_TS_UNCHECKED_READ(root.empty_slot_spans_dirty_bytes), - single_slot_count * single_slot_size); - - // Nothing left after explicit purge. - root.PurgeMemory(PurgeFlags::kDecommitEmptySlotSpans); - EXPECT_EQ(PA_TS_UNCHECKED_READ(root.empty_slot_spans_dirty_bytes), 0u); - - for (void* ptr : allocated_memory) { - root.Free(ptr); - } -} - -TEST_P(PartitionAllocTest, IncreaseEmptySlotSpanRingSize) { - PartitionRoot<ThreadSafe> root({ - PartitionOptions::AlignedAlloc::kDisallowed, - PartitionOptions::ThreadCache::kDisabled, - PartitionOptions::Quarantine::kDisallowed, - PartitionOptions::Cookie::kAllowed, - PartitionOptions::BackupRefPtr::kDisabled, - PartitionOptions::BackupRefPtrZapping::kDisabled, - PartitionOptions::UseConfigurablePool::kIfAvailable, - }); - root.UncapEmptySlotSpanMemoryForTesting(); - SetDistributionForPartitionRoot(&root, GetBucketDistribution()); - - std::vector<void*> single_slot_allocated_memory; - constexpr size_t single_slot_count = kDefaultEmptySlotSpanRingSize + 10; - const size_t single_slot_size = MaxRegularSlotSpanSize() + 1; - const size_t bucket_size = - root.buckets[SizeToIndex(single_slot_size)].slot_size; - - for (size_t i = 0; i < single_slot_count; i++) { - void* ptr = root.Alloc(single_slot_size, ""); - single_slot_allocated_memory.push_back(ptr); - } - - // Free everything at once, creating as many empty slot spans as there are - // allocations (since they are from single-slot slot spans). - for (void* ptr : single_slot_allocated_memory) { - root.Free(ptr); - } - single_slot_allocated_memory.clear(); - - // Some of the free()-s above overflowed the slot span ring. - EXPECT_EQ(PA_TS_UNCHECKED_READ(root.empty_slot_spans_dirty_bytes), - kDefaultEmptySlotSpanRingSize * bucket_size); - - // Now can cache more slot spans. - root.EnableLargeEmptySlotSpanRing(); - - constexpr size_t single_slot_large_count = kDefaultEmptySlotSpanRingSize + 10; - for (size_t i = 0; i < single_slot_large_count; i++) { - void* ptr = root.Alloc(single_slot_size, ""); - single_slot_allocated_memory.push_back(ptr); - } - - for (void* ptr : single_slot_allocated_memory) { - root.Free(ptr); - } - single_slot_allocated_memory.clear(); - - // No overflow this time. - EXPECT_EQ(PA_TS_UNCHECKED_READ(root.empty_slot_spans_dirty_bytes), - single_slot_large_count * bucket_size); - - constexpr size_t single_slot_too_many_count = kMaxFreeableSpans + 10; - for (size_t i = 0; i < single_slot_too_many_count; i++) { - void* ptr = root.Alloc(single_slot_size, ""); - single_slot_allocated_memory.push_back(ptr); - } - - for (void* ptr : single_slot_allocated_memory) { - root.Free(ptr); - } - single_slot_allocated_memory.clear(); - - // Overflow still works. - EXPECT_EQ(PA_TS_UNCHECKED_READ(root.empty_slot_spans_dirty_bytes), - kMaxFreeableSpans * bucket_size); -} - -#if BUILDFLAG(PA_IS_CAST_ANDROID) && BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -extern "C" { -void* __real_malloc(size_t); -} // extern "C" - -TEST_P(PartitionAllocTest, HandleMixedAllocations) { - void* ptr = __real_malloc(12); - // Should not crash, no test assertion. - free(ptr); -} -#endif - -TEST_P(PartitionAllocTest, SortFreelist) { - const size_t count = 100; - const size_t allocation_size = 1; - void* first_ptr = allocator.root()->Alloc(allocation_size, ""); - - std::vector<void*> allocations; - for (size_t i = 0; i < count; ++i) { - allocations.push_back(allocator.root()->Alloc(allocation_size, "")); - } - - // Shuffle and free memory out of order. - std::random_device rd; - std::mt19937 generator(rd()); - std::shuffle(allocations.begin(), allocations.end(), generator); - - // Keep one allocation alive (first_ptr), so that the SlotSpan is not fully - // empty. - for (void* ptr : allocations) { - allocator.root()->Free(ptr); - } - allocations.clear(); - - allocator.root()->PurgeMemory(PurgeFlags::kDiscardUnusedSystemPages); - - size_t bucket_index = - SizeToIndex(allocation_size + ExtraAllocSize(allocator)); - auto& bucket = allocator.root()->buckets[bucket_index]; - EXPECT_TRUE(bucket.active_slot_spans_head->freelist_is_sorted()); - - // Can sort again. - allocator.root()->PurgeMemory(PurgeFlags::kDiscardUnusedSystemPages); - EXPECT_TRUE(bucket.active_slot_spans_head->freelist_is_sorted()); - - for (size_t i = 0; i < count; ++i) { - allocations.push_back(allocator.root()->Alloc(allocation_size, "")); - // Allocating keeps the freelist sorted. - EXPECT_TRUE(bucket.active_slot_spans_head->freelist_is_sorted()); - } - - // Check that it is sorted. - for (size_t i = 1; i < allocations.size(); i++) { - EXPECT_LT(UntagPtr(allocations[i - 1]), UntagPtr(allocations[i])); - } - - for (void* ptr : allocations) { - allocator.root()->Free(ptr); - // Free()-ing memory destroys order. Not looking at the head of the active - // list, as it is not necessarily the one from which |ptr| came from. - auto* slot_span = - SlotSpan::FromSlotStart(allocator.root()->ObjectToSlotStart(ptr)); - EXPECT_FALSE(slot_span->freelist_is_sorted()); - } - - allocator.root()->Free(first_ptr); -} - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && BUILDFLAG(IS_LINUX) && \ - defined(ARCH_CPU_64_BITS) -TEST_P(PartitionAllocTest, CrashOnUnknownPointer) { - int not_a_heap_object = 42; - EXPECT_DEATH(allocator.root()->Free(¬_a_heap_object), ""); -} -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && - // BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_64_BITS) - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && BUILDFLAG(IS_MAC) - -// Adapted from crashpad tests. -class ScopedOpenCLNoOpKernel { - public: - ScopedOpenCLNoOpKernel() - : context_(nullptr), - program_(nullptr), - kernel_(nullptr), - success_(false) {} - - ScopedOpenCLNoOpKernel(const ScopedOpenCLNoOpKernel&) = delete; - ScopedOpenCLNoOpKernel& operator=(const ScopedOpenCLNoOpKernel&) = delete; - - ~ScopedOpenCLNoOpKernel() { - if (kernel_) { - cl_int rv = clReleaseKernel(kernel_); - EXPECT_EQ(rv, CL_SUCCESS) << "clReleaseKernel"; - } - - if (program_) { - cl_int rv = clReleaseProgram(program_); - EXPECT_EQ(rv, CL_SUCCESS) << "clReleaseProgram"; - } - - if (context_) { - cl_int rv = clReleaseContext(context_); - EXPECT_EQ(rv, CL_SUCCESS) << "clReleaseContext"; - } - } - - void SetUp() { - cl_platform_id platform_id; - cl_int rv = clGetPlatformIDs(1, &platform_id, nullptr); - ASSERT_EQ(rv, CL_SUCCESS) << "clGetPlatformIDs"; - cl_device_id device_id; - rv = - clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_CPU, 1, &device_id, nullptr); -#if defined(ARCH_CPU_ARM64) - // CL_DEVICE_TYPE_CPU doesn’t seem to work at all on arm64, meaning that - // these weird OpenCL modules probably don’t show up there at all. Keep this - // test even on arm64 in case this ever does start working. - if (rv == CL_INVALID_VALUE) { - return; - } -#endif // ARCH_CPU_ARM64 - ASSERT_EQ(rv, CL_SUCCESS) << "clGetDeviceIDs"; - - context_ = clCreateContext(nullptr, 1, &device_id, nullptr, nullptr, &rv); - ASSERT_EQ(rv, CL_SUCCESS) << "clCreateContext"; - - const char* sources[] = { - "__kernel void NoOp(void) {barrier(CLK_LOCAL_MEM_FENCE);}", - }; - const size_t source_lengths[] = { - strlen(sources[0]), - }; - static_assert(std::size(sources) == std::size(source_lengths), - "arrays must be parallel"); - - program_ = clCreateProgramWithSource(context_, std::size(sources), sources, - source_lengths, &rv); - ASSERT_EQ(rv, CL_SUCCESS) << "clCreateProgramWithSource"; - - rv = clBuildProgram(program_, 1, &device_id, "-cl-opt-disable", nullptr, - nullptr); - ASSERT_EQ(rv, CL_SUCCESS) << "clBuildProgram"; - - kernel_ = clCreateKernel(program_, "NoOp", &rv); - ASSERT_EQ(rv, CL_SUCCESS) << "clCreateKernel"; - - success_ = true; - } - - bool success() const { return success_; } - - private: - cl_context context_; - cl_program program_; - cl_kernel kernel_; - bool success_; -}; - -// On macOS 10.11, allocations are made with PartitionAlloc, but the pointer -// is incorrectly passed by CoreFoundation to the previous default zone, -// causing crashes. This is intended to detect these issues regressing in future -// versions of macOS. -TEST_P(PartitionAllocTest, OpenCL) { - ScopedOpenCLNoOpKernel kernel; - kernel.SetUp(); -#if !defined(ARCH_CPU_ARM64) - ASSERT_TRUE(kernel.success()); -#endif -} - -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && - // BUILDFLAG(IS_MAC) - -TEST_P(PartitionAllocTest, SmallSlotSpanWaste) { - for (PartitionRoot<ThreadSafe>::Bucket& bucket : allocator.root()->buckets) { - const size_t slot_size = bucket.slot_size; - if (slot_size == kInvalidBucketSize) { - continue; - } - - size_t small_system_page_count = - partition_alloc::internal::ComputeSystemPagesPerSlotSpan( - bucket.slot_size, true); - size_t small_waste = - (small_system_page_count * SystemPageSize()) % slot_size; - - EXPECT_LT(small_waste, .05 * SystemPageSize()); - if (slot_size <= MaxRegularSlotSpanSize()) { - EXPECT_LE(small_system_page_count, MaxSystemPagesPerRegularSlotSpan()); - } - } -} - -TEST_P(PartitionAllocTest, SortActiveSlotSpans) { - auto run_test = [](size_t count) { - PartitionBucket<ThreadSafe> bucket; - bucket.Init(16); - bucket.active_slot_spans_head = nullptr; - - std::vector<SlotSpanMetadata<ThreadSafe>> slot_spans; - slot_spans.reserve(count); - - // Add slot spans with random freelist length. - for (size_t i = 0; i < count; i++) { - slot_spans.emplace_back(&bucket); - auto& slot_span = slot_spans.back(); - slot_span.num_unprovisioned_slots = - partition_alloc::internal::base::RandGenerator( - bucket.get_slots_per_span() / 2); - slot_span.num_allocated_slots = - partition_alloc::internal::base::RandGenerator( - bucket.get_slots_per_span() - slot_span.num_unprovisioned_slots); - slot_span.next_slot_span = bucket.active_slot_spans_head; - bucket.active_slot_spans_head = &slot_span; - } - - bucket.SortActiveSlotSpans(); - - std::set<SlotSpanMetadata<ThreadSafe>*> seen_slot_spans; - std::vector<SlotSpanMetadata<ThreadSafe>*> sorted_slot_spans; - for (auto* slot_span = bucket.active_slot_spans_head; slot_span; - slot_span = slot_span->next_slot_span) { - sorted_slot_spans.push_back(slot_span); - seen_slot_spans.insert(slot_span); - } - - // None repeated, none missing. - EXPECT_EQ(seen_slot_spans.size(), sorted_slot_spans.size()); - EXPECT_EQ(seen_slot_spans.size(), slot_spans.size()); - - // The first slot spans are sorted. - size_t sorted_spans_count = - std::min(PartitionBucket<ThreadSafe>::kMaxSlotSpansToSort, count); - EXPECT_TRUE(std::is_sorted(sorted_slot_spans.begin(), - sorted_slot_spans.begin() + sorted_spans_count, - partition_alloc::internal::CompareSlotSpans)); - - // Slot spans with no freelist entries are at the end of the sorted run. - auto has_empty_freelist = [](SlotSpanMetadata<ThreadSafe>* a) { - return a->GetFreelistLength() == 0; - }; - auto it = std::find_if(sorted_slot_spans.begin(), - sorted_slot_spans.begin() + sorted_spans_count, - has_empty_freelist); - if (it != sorted_slot_spans.end()) { - EXPECT_TRUE(std::all_of(it, - sorted_slot_spans.begin() + sorted_spans_count, - has_empty_freelist)); - } - }; - - // Everything is sorted. - run_test(PartitionBucket<ThreadSafe>::kMaxSlotSpansToSort / 2); - // Only the first slot spans are sorted. - run_test(PartitionBucket<ThreadSafe>::kMaxSlotSpansToSort * 2); - - // Corner cases. - run_test(0); - run_test(1); -} - -#if BUILDFLAG(USE_FREESLOT_BITMAP) -TEST_P(PartitionAllocTest, FreeSlotBitmapMarkedAsUsedAfterAlloc) { - void* ptr = allocator.root()->Alloc(kTestAllocSize, type_name); - uintptr_t slot_start = allocator.root()->ObjectToSlotStart(ptr); - EXPECT_TRUE(FreeSlotBitmapSlotIsUsed(slot_start)); - - allocator.root()->Free(ptr); -} - -TEST_P(PartitionAllocTest, FreeSlotBitmapMarkedAsFreeAfterFree) { - void* ptr = allocator.root()->Alloc(kTestAllocSize, type_name); - uintptr_t slot_start = allocator.root()->ObjectToSlotStart(ptr); - EXPECT_TRUE(FreeSlotBitmapSlotIsUsed(slot_start)); - - allocator.root()->Free(ptr); - EXPECT_FALSE(FreeSlotBitmapSlotIsUsed(slot_start)); -} - -TEST_P(PartitionAllocTest, FreeSlotBitmapResetAfterDecommit) { - void* ptr1 = allocator.root()->Alloc( - SystemPageSize() - ExtraAllocSize(allocator), type_name); - uintptr_t slot_start = allocator.root()->ObjectToSlotStart(ptr1); - allocator.root()->Free(ptr1); - - EXPECT_FALSE(FreeSlotBitmapSlotIsUsed(slot_start)); - // Decommit the slot span. Bitmap will be rewritten in Decommit(). - allocator.root()->PurgeMemory(PurgeFlags::kDecommitEmptySlotSpans); - EXPECT_TRUE(FreeSlotBitmapSlotIsUsed(slot_start)); -} - -TEST_P(PartitionAllocTest, FreeSlotBitmapResetAfterPurge) { - void* ptr1 = allocator.root()->Alloc( - SystemPageSize() - ExtraAllocSize(allocator), type_name); - char* ptr2 = static_cast<char*>(allocator.root()->Alloc( - SystemPageSize() - ExtraAllocSize(allocator), type_name)); - uintptr_t slot_start = allocator.root()->ObjectToSlotStart(ptr2); - allocator.root()->Free(ptr2); - - CHECK_PAGE_IN_CORE(ptr2 - kPointerOffset, true); - EXPECT_FALSE(FreeSlotBitmapSlotIsUsed(slot_start)); - // Bitmap will be rewritten in PartitionPurgeSlotSpan(). - allocator.root()->PurgeMemory(PurgeFlags::kDiscardUnusedSystemPages); - CHECK_PAGE_IN_CORE(ptr2 - kPointerOffset, false); - EXPECT_TRUE(FreeSlotBitmapSlotIsUsed(slot_start)); - - allocator.root()->Free(ptr1); -} - -#endif // BUILDFLAG(USE_FREESLOT_BITMAP) - -} // namespace partition_alloc::internal - -#endif // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
diff --git a/base/allocator/partition_allocator/partition_bucket.cc b/base/allocator/partition_allocator/partition_bucket.cc deleted file mode 100644 index 7236a17..0000000 --- a/base/allocator/partition_allocator/partition_bucket.cc +++ /dev/null
@@ -1,1485 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_bucket.h" - -#include <algorithm> -#include <cstdint> -#include <tuple> - -#include "base/allocator/partition_allocator/address_pool_manager.h" -#include "base/allocator/partition_allocator/freeslot_bitmap.h" -#include "base/allocator/partition_allocator/freeslot_bitmap_constants.h" -#include "base/allocator/partition_allocator/oom.h" -#include "base/allocator/partition_allocator/page_allocator.h" -#include "base/allocator/partition_allocator/page_allocator_constants.h" -#include "base/allocator/partition_allocator/partition_address_space.h" -#include "base/allocator/partition_allocator/partition_alloc.h" -#include "base/allocator/partition_allocator/partition_alloc_base/bits.h" -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/alias.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h" -#include "base/allocator/partition_allocator/partition_alloc_base/thread_annotations.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/partition_alloc_config.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "base/allocator/partition_allocator/partition_alloc_forward.h" -#include "base/allocator/partition_allocator/partition_direct_map_extent.h" -#include "base/allocator/partition_allocator/partition_oom.h" -#include "base/allocator/partition_allocator/partition_page.h" -#include "base/allocator/partition_allocator/reservation_offset_table.h" -#include "base/allocator/partition_allocator/tagging.h" -#include "build/build_config.h" - -#if BUILDFLAG(USE_STARSCAN) -#include "base/allocator/partition_allocator/starscan/pcscan.h" -#endif - -namespace partition_alloc::internal { - -namespace { - -#if PA_CONFIG(ENABLE_SHADOW_METADATA) -PA_ALWAYS_INLINE uintptr_t ShadowMetadataStart(uintptr_t super_page, - pool_handle pool) { - uintptr_t shadow_metadata_start = - super_page + SystemPageSize() + ShadowPoolOffset(pool); - PA_DCHECK(!PartitionAddressSpace::IsInRegularPool(shadow_metadata_start)); - PA_DCHECK(!PartitionAddressSpace::IsInBRPPool(shadow_metadata_start)); - return shadow_metadata_start; -} -#endif - -template <bool thread_safe> -[[noreturn]] PA_NOINLINE void PartitionOutOfMemoryMappingFailure( - PartitionRoot<thread_safe>* root, - size_t size) PA_LOCKS_EXCLUDED(root->lock_) { - PA_NO_CODE_FOLDING(); - root->OutOfMemory(size); - PA_IMMEDIATE_CRASH(); // Not required, kept as documentation. -} - -template <bool thread_safe> -[[noreturn]] PA_NOINLINE void PartitionOutOfMemoryCommitFailure( - PartitionRoot<thread_safe>* root, - size_t size) PA_LOCKS_EXCLUDED(root->lock_) { - PA_NO_CODE_FOLDING(); - root->OutOfMemory(size); - PA_IMMEDIATE_CRASH(); // Not required, kept as documentation. -} - -#if !BUILDFLAG(HAS_64_BIT_POINTERS) && BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) -// |start| has to be aligned to kSuperPageSize, but |end| doesn't. This means -// that a partial super page is allowed at the end. Since the block list uses -// kSuperPageSize granularity, a partial super page is considered blocked if -// there is a raw_ptr<T> pointing anywhere in that super page, even if doesn't -// point to that partially allocated region. -bool AreAllowedSuperPagesForBRPPool(uintptr_t start, uintptr_t end) { - PA_DCHECK(!(start % kSuperPageSize)); - for (uintptr_t super_page = start; super_page < end; - super_page += kSuperPageSize) { - // If any blocked super page is found inside the given memory region, - // the memory region is blocked. - if (!AddressPoolManagerBitmap::IsAllowedSuperPageForBRPPool(super_page)) { - AddressPoolManagerBitmap::IncrementBlocklistHitCount(); - return false; - } - } - return true; -} -#endif // !BUILDFLAG(HAS_64_BIT_POINTERS) && - // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - -// Reserves |requested_size| worth of super pages from the specified pool. -// If BRP pool is requested this function will honor BRP block list. -// -// The returned address will be aligned to kSuperPageSize, and so -// |requested_address| should be. |requested_size| doesn't have to be, however. -// -// |requested_address| is merely a hint, which will be attempted, but easily -// given up on if doesn't work the first time. -// -// The function doesn't need to hold root->lock_ or any other locks, because: -// - It (1) reserves memory, (2) then consults AreAllowedSuperPagesForBRPPool -// for that memory, and (3) returns the memory if -// allowed, or unreserves and decommits if not allowed. So no other -// overlapping region can be allocated while executing -// AreAllowedSuperPagesForBRPPool. -// - IsAllowedSuperPageForBRPPool (used by AreAllowedSuperPagesForBRPPool) is -// designed to not need locking. -uintptr_t ReserveMemoryFromPool(pool_handle pool, - uintptr_t requested_address, - size_t requested_size) { - PA_DCHECK(!(requested_address % kSuperPageSize)); - - uintptr_t reserved_address = AddressPoolManager::GetInstance().Reserve( - pool, requested_address, requested_size); - - // In 32-bit mode, when allocating from BRP pool, verify that the requested - // allocation honors the block list. Find a better address otherwise. -#if !BUILDFLAG(HAS_64_BIT_POINTERS) && BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - if (pool == kBRPPoolHandle) { - constexpr int kMaxRandomAddressTries = 10; - for (int i = 0; i < kMaxRandomAddressTries; ++i) { - if (!reserved_address || - AreAllowedSuperPagesForBRPPool(reserved_address, - reserved_address + requested_size)) { - break; - } - AddressPoolManager::GetInstance().UnreserveAndDecommit( - pool, reserved_address, requested_size); - // No longer try to honor |requested_address|, because it didn't work for - // us last time. - reserved_address = - AddressPoolManager::GetInstance().Reserve(pool, 0, requested_size); - } - - // If the allocation attempt succeeds, we will break out of the following - // loop immediately. - // - // Last resort: sequentially scan the whole 32-bit address space. The number - // of blocked super-pages should be very small, so we expect to practically - // never need to run the following code. Note that it may fail to find an - // available super page, e.g., when it becomes available after the scan - // passes through it, but we accept the risk. - for (uintptr_t address_to_try = kSuperPageSize; address_to_try != 0; - address_to_try += kSuperPageSize) { - if (!reserved_address || - AreAllowedSuperPagesForBRPPool(reserved_address, - reserved_address + requested_size)) { - break; - } - AddressPoolManager::GetInstance().UnreserveAndDecommit( - pool, reserved_address, requested_size); - // Reserve() can return a different pointer than attempted. - reserved_address = AddressPoolManager::GetInstance().Reserve( - pool, address_to_try, requested_size); - } - - // If the loop ends naturally, the last allocated region hasn't been - // verified. Do it now. - if (reserved_address && - !AreAllowedSuperPagesForBRPPool(reserved_address, - reserved_address + requested_size)) { - AddressPoolManager::GetInstance().UnreserveAndDecommit( - pool, reserved_address, requested_size); - reserved_address = 0; - } - } -#endif // !BUILDFLAG(HAS_64_BIT_POINTERS) && - // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - -#if !BUILDFLAG(HAS_64_BIT_POINTERS) - // Only mark the region as belonging to the pool after it has passed the - // blocklist check in order to avoid a potential race with destructing a - // raw_ptr<T> object that points to non-PA memory in another thread. - // If `MarkUsed` was called earlier, the other thread could incorrectly - // determine that the allocation had come form PartitionAlloc. - if (reserved_address) { - AddressPoolManager::GetInstance().MarkUsed(pool, reserved_address, - requested_size); - } -#endif - - PA_DCHECK(!(reserved_address % kSuperPageSize)); - return reserved_address; -} - -template <bool thread_safe> -SlotSpanMetadata<thread_safe>* PartitionDirectMap( - PartitionRoot<thread_safe>* root, - unsigned int flags, - size_t raw_size, - size_t slot_span_alignment) { - PA_DCHECK((slot_span_alignment >= PartitionPageSize()) && - base::bits::IsPowerOfTwo(slot_span_alignment)); - - // No static EXCLUSIVE_LOCKS_REQUIRED(), as the checker doesn't understand - // scoped unlocking. - root->lock_.AssertAcquired(); - - const bool return_null = flags & AllocFlags::kReturnNull; - if (PA_UNLIKELY(raw_size > MaxDirectMapped())) { - if (return_null) { - return nullptr; - } - - // The lock is here to protect PA from: - // 1. Concurrent calls - // 2. Reentrant calls - // - // This is fine here however, as: - // 1. Concurrency: |PartitionRoot::OutOfMemory()| never returns, so the lock - // will not be re-acquired, which would lead to acting on inconsistent - // data that could have been modified in-between releasing and acquiring - // it. - // 2. Reentrancy: This is why we release the lock. On some platforms, - // terminating the process may free() memory, or even possibly try to - // allocate some. Calling free() is fine, but will deadlock since - // |PartitionRoot::lock_| is not recursive. - // - // Supporting reentrant calls properly is hard, and not a requirement for - // PA. However up to that point, we've only *read* data, not *written* to - // any state. Reentrant calls are then fine, especially as we don't continue - // on this path. The only downside is possibly endless recursion if the OOM - // handler allocates and fails to use UncheckedMalloc() or equivalent, but - // that's violating the contract of base::TerminateBecauseOutOfMemory(). - ScopedUnlockGuard unlock{root->lock_}; - PartitionExcessiveAllocationSize(raw_size); - } - - PartitionDirectMapExtent<thread_safe>* map_extent = nullptr; - PartitionPage<thread_safe>* page = nullptr; - - { - // Getting memory for direct-mapped allocations doesn't interact with the - // rest of the allocator, but takes a long time, as it involves several - // system calls. Although no mmap() (or equivalent) calls are made on - // 64 bit systems, page permissions are changed with mprotect(), which is - // a syscall. - // - // These calls are almost always slow (at least a couple us per syscall on a - // desktop Linux machine), and they also have a very long latency tail, - // possibly from getting descheduled. As a consequence, we should not hold - // the lock when performing a syscall. This is not the only problematic - // location, but since this one doesn't interact with the rest of the - // allocator, we can safely drop and then re-acquire the lock. - // - // Note that this only affects allocations that are not served out of the - // thread cache, but as a simple example the buffer partition in blink is - // frequently used for large allocations (e.g. ArrayBuffer), and frequent, - // small ones (e.g. WTF::String), and does not have a thread cache. - ScopedUnlockGuard scoped_unlock{root->lock_}; - - const size_t slot_size = - PartitionRoot<thread_safe>::GetDirectMapSlotSize(raw_size); - // The super page starts with a partition page worth of metadata and guard - // pages, hence alignment requests ==PartitionPageSize() will be - // automatically satisfied. Padding is needed for higher-order alignment - // requests. Note, |slot_span_alignment| is at least 1 partition page. - const size_t padding_for_alignment = - slot_span_alignment - PartitionPageSize(); - const size_t reservation_size = - PartitionRoot<thread_safe>::GetDirectMapReservationSize( - raw_size + padding_for_alignment); -#if BUILDFLAG(PA_DCHECK_IS_ON) - const size_t available_reservation_size = - reservation_size - padding_for_alignment - - PartitionRoot<thread_safe>::GetDirectMapMetadataAndGuardPagesSize(); - PA_DCHECK(slot_size <= available_reservation_size); -#endif - - pool_handle pool = root->ChoosePool(); - uintptr_t reservation_start; - { - // Reserving memory from the pool is actually not a syscall on 64 bit - // platforms. -#if !BUILDFLAG(HAS_64_BIT_POINTERS) - ScopedSyscallTimer timer{root}; -#endif - reservation_start = ReserveMemoryFromPool(pool, 0, reservation_size); - } - if (PA_UNLIKELY(!reservation_start)) { - if (return_null) { - return nullptr; - } - - PartitionOutOfMemoryMappingFailure(root, reservation_size); - } - - root->total_size_of_direct_mapped_pages.fetch_add( - reservation_size, std::memory_order_relaxed); - - // Shift by 1 partition page (metadata + guard pages) and alignment padding. - const uintptr_t slot_start = - reservation_start + PartitionPageSize() + padding_for_alignment; - - { - ScopedSyscallTimer timer{root}; - RecommitSystemPages(reservation_start + SystemPageSize(), - SystemPageSize(), -#if PA_CONFIG(ENABLE_SHADOW_METADATA) - root->PageAccessibilityWithPkeyIfEnabled( - PageAccessibilityConfiguration::kRead), -#else - root->PageAccessibilityWithPkeyIfEnabled( - PageAccessibilityConfiguration::kReadWrite), -#endif - PageAccessibilityDisposition::kRequireUpdate); - } - -#if BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) - // If PUT_REF_COUNT_IN_PREVIOUS_SLOT is on, and if the BRP pool is - // used, allocate a SystemPage for RefCount "bitmap" (only one of its - // elements will be used). - if (pool == kBRPPoolHandle) { - ScopedSyscallTimer timer{root}; - RecommitSystemPages(reservation_start + SystemPageSize() * 2, - SystemPageSize(), - root->PageAccessibilityWithPkeyIfEnabled( - PageAccessibilityConfiguration::kReadWrite), - PageAccessibilityDisposition::kRequireUpdate); - } -#endif - -#if PA_CONFIG(ENABLE_SHADOW_METADATA) - { - ScopedSyscallTimer timer{root}; - RecommitSystemPages(ShadowMetadataStart(reservation_start, pool), - SystemPageSize(), - root->PageAccessibilityWithPkeyIfEnabled( - PageAccessibilityConfiguration::kReadWrite), - PageAccessibilityDisposition::kRequireUpdate); - } -#endif - - // No need to hold root->lock_. Now that memory is reserved, no other - // overlapping region can be allocated (because of how pools work), - // so no other thread can update the same offset table entries at the - // same time. Furthermore, nobody will be ready these offsets until this - // function returns. - uintptr_t address_start = reservation_start; - uintptr_t address_end = address_start + reservation_size; - auto* offset_ptr = ReservationOffsetPointer(address_start); - uint16_t offset = 0; - while (address_start < address_end) { - PA_DCHECK(offset_ptr < GetReservationOffsetTableEnd(address_start)); - PA_DCHECK(offset < kOffsetTagNormalBuckets); - *offset_ptr++ = offset++; - address_start += kSuperPageSize; - } - - auto* super_page_extent = - PartitionSuperPageToExtent<thread_safe>(reservation_start); - super_page_extent->root = root; - // The new structures are all located inside a fresh system page so they - // will all be zeroed out. These DCHECKs are for documentation and to assert - // our expectations of the kernel. - PA_DCHECK(!super_page_extent->number_of_consecutive_super_pages); - PA_DCHECK(!super_page_extent->next); - - PartitionPage<thread_safe>* first_page = - reinterpret_cast<PartitionPage<thread_safe>*>(super_page_extent) + 1; - page = PartitionPage<thread_safe>::FromAddr(slot_start); - // |first_page| and |page| may be equal, if there is no alignment padding. - if (page != first_page) { - PA_DCHECK(page > first_page); - PA_DCHECK(page - first_page <= - PartitionPage<thread_safe>::kMaxSlotSpanMetadataOffset); - PA_CHECK(!first_page->is_valid); - first_page->has_valid_span_after_this = true; - first_page->slot_span_metadata_offset = page - first_page; - } - auto* metadata = - reinterpret_cast<PartitionDirectMapMetadata<thread_safe>*>(page); - // Since direct map metadata is larger than PartitionPage, make sure the - // first and the last bytes are on the same system page, i.e. within the - // super page metadata region. - PA_DCHECK(base::bits::AlignDown(reinterpret_cast<uintptr_t>(metadata), - SystemPageSize()) == - base::bits::AlignDown( - reinterpret_cast<uintptr_t>(metadata) + - sizeof(PartitionDirectMapMetadata<thread_safe>) - 1, - SystemPageSize())); - PA_DCHECK(page == &metadata->page); - page->is_valid = true; - PA_DCHECK(!page->has_valid_span_after_this); - PA_DCHECK(!page->slot_span_metadata_offset); - PA_DCHECK(!page->slot_span_metadata.next_slot_span); - PA_DCHECK(!page->slot_span_metadata.marked_full); - PA_DCHECK(!page->slot_span_metadata.num_allocated_slots); - PA_DCHECK(!page->slot_span_metadata.num_unprovisioned_slots); - PA_DCHECK(!page->slot_span_metadata.in_empty_cache()); - - PA_DCHECK(!metadata->subsequent_page.subsequent_page_metadata.raw_size); - // Raw size is set later, by the caller. - metadata->subsequent_page.slot_span_metadata_offset = 1; - - PA_DCHECK(!metadata->bucket.active_slot_spans_head); - PA_DCHECK(!metadata->bucket.empty_slot_spans_head); - PA_DCHECK(!metadata->bucket.decommitted_slot_spans_head); - PA_DCHECK(!metadata->bucket.num_system_pages_per_slot_span); - PA_DCHECK(!metadata->bucket.num_full_slot_spans); - metadata->bucket.slot_size = slot_size; - - new (&page->slot_span_metadata) - SlotSpanMetadata<thread_safe>(&metadata->bucket); - - // It is typically possible to map a large range of inaccessible pages, and - // this is leveraged in multiple places, including the pools. However, - // this doesn't mean that we can commit all this memory. For the vast - // majority of allocations, this just means that we crash in a slightly - // different place, but for callers ready to handle failures, we have to - // return nullptr. See crbug.com/1187404. - // - // Note that we didn't check above, because if we cannot even commit a - // single page, then this is likely hopeless anyway, and we will crash very - // soon. - const bool ok = root->TryRecommitSystemPagesForData( - slot_start, slot_size, PageAccessibilityDisposition::kRequireUpdate); - if (!ok) { - if (!return_null) { - PartitionOutOfMemoryCommitFailure(root, slot_size); - } - - { - ScopedSyscallTimer timer{root}; -#if !BUILDFLAG(HAS_64_BIT_POINTERS) - AddressPoolManager::GetInstance().MarkUnused(pool, reservation_start, - reservation_size); -#endif - AddressPoolManager::GetInstance().UnreserveAndDecommit( - pool, reservation_start, reservation_size); - } - - root->total_size_of_direct_mapped_pages.fetch_sub( - reservation_size, std::memory_order_relaxed); - - return nullptr; - } - - auto* next_entry = PartitionFreelistEntry::EmplaceAndInitNull(slot_start); - page->slot_span_metadata.SetFreelistHead(next_entry); - - map_extent = &metadata->direct_map_extent; - map_extent->reservation_size = reservation_size; - map_extent->padding_for_alignment = padding_for_alignment; - map_extent->bucket = &metadata->bucket; - } - - root->lock_.AssertAcquired(); - - // Maintain the doubly-linked list of all direct mappings. - map_extent->next_extent = root->direct_map_list; - if (map_extent->next_extent) { - map_extent->next_extent->prev_extent = map_extent; - } - map_extent->prev_extent = nullptr; - root->direct_map_list = map_extent; - - return &page->slot_span_metadata; -} - -uint8_t ComputeSystemPagesPerSlotSpanPreferSmall(size_t slot_size) { - if (slot_size > MaxRegularSlotSpanSize()) { - // This is technically not needed, as for now all the larger slot sizes are - // multiples of the system page size. - return base::bits::AlignUp(slot_size, SystemPageSize()) / SystemPageSize(); - } - - // Smaller slot spans waste less address space, as well as potentially lower - // fragmentation: - // - Address space: This comes from fuller SuperPages (since the tail end of a - // SuperPage is more likely to be used when the slot span is smaller. Also, - // if a slot span is partially used, a smaller slot span will use less - // address space. - // - In-slot fragmentation: Slot span management code will prioritize - // almost-full slot spans, as well as trying to keep empty slot spans - // empty. The more granular this logic can work, the better. - // - // Since metadata space overhead is constant per-PartitionPage, keeping - // smaller slot spans makes sense. - // - // Underlying memory allocation is done per-PartitionPage, but memory commit - // is done per system page. This means that we prefer to fill the entirety of - // a PartitionPage with a slot span, but we can tolerate some system pages - // being empty at the end, as these will not cost committed or dirty memory. - // - // The choice below is, for multi-slot slot spans: - // - If a full PartitionPage slot span is possible with less than 2% of a - // *single* system page wasted, use it. The smallest possible size wins. - // - Otherwise, select the size with the smallest virtual address space - // loss. Allow a SlotSpan to leave some slack in its PartitionPage, up to - // 1/4 of the total. - for (size_t partition_page_count = 1; - partition_page_count <= kMaxPartitionPagesPerRegularSlotSpan; - partition_page_count++) { - size_t candidate_size = partition_page_count * PartitionPageSize(); - size_t waste = candidate_size % slot_size; - if (waste <= .02 * SystemPageSize()) { - return partition_page_count * NumSystemPagesPerPartitionPage(); - } - } - - size_t best_count = 0; - size_t best_waste = std::numeric_limits<size_t>::max(); - for (size_t partition_page_count = 1; - partition_page_count <= kMaxPartitionPagesPerRegularSlotSpan; - partition_page_count++) { - // Prefer no slack. - for (size_t slack = 0; slack < partition_page_count; slack++) { - size_t system_page_count = - partition_page_count * NumSystemPagesPerPartitionPage() - slack; - size_t candidate_size = system_page_count * SystemPageSize(); - size_t waste = candidate_size % slot_size; - if (waste < best_waste) { - best_waste = waste; - best_count = system_page_count; - } - } - } - return best_count; -} - -uint8_t ComputeSystemPagesPerSlotSpanInternal(size_t slot_size) { - // This works out reasonably for the current bucket sizes of the generic - // allocator, and the current values of partition page size and constants. - // Specifically, we have enough room to always pack the slots perfectly into - // some number of system pages. The only waste is the waste associated with - // unfaulted pages (i.e. wasted address space). - // TODO: we end up using a lot of system pages for very small sizes. For - // example, we'll use 12 system pages for slot size 24. The slot size is so - // small that the waste would be tiny with just 4, or 1, system pages. Later, - // we can investigate whether there are anti-fragmentation benefits to using - // fewer system pages. - double best_waste_ratio = 1.0f; - uint16_t best_pages = 0; - if (slot_size > MaxRegularSlotSpanSize()) { - // TODO(ajwong): Why is there a DCHECK here for this? - // http://crbug.com/776537 - PA_DCHECK(!(slot_size % SystemPageSize())); - best_pages = static_cast<uint16_t>(slot_size >> SystemPageShift()); - PA_CHECK(best_pages <= std::numeric_limits<uint8_t>::max()); - return static_cast<uint8_t>(best_pages); - } - PA_DCHECK(slot_size <= MaxRegularSlotSpanSize()); - for (uint16_t i = NumSystemPagesPerPartitionPage() - 1; - i <= MaxSystemPagesPerRegularSlotSpan(); ++i) { - size_t page_size = i << SystemPageShift(); - size_t num_slots = page_size / slot_size; - size_t waste = page_size - (num_slots * slot_size); - // Leaving a page unfaulted is not free; the page will occupy an empty page - // table entry. Make a simple attempt to account for that. - // - // TODO(ajwong): This looks wrong. PTEs are allocated for all pages - // regardless of whether or not they are wasted. Should it just - // be waste += i * sizeof(void*)? - // http://crbug.com/776537 - size_t num_remainder_pages = i & (NumSystemPagesPerPartitionPage() - 1); - size_t num_unfaulted_pages = - num_remainder_pages - ? (NumSystemPagesPerPartitionPage() - num_remainder_pages) - : 0; - waste += sizeof(void*) * num_unfaulted_pages; - double waste_ratio = - static_cast<double>(waste) / static_cast<double>(page_size); - if (waste_ratio < best_waste_ratio) { - best_waste_ratio = waste_ratio; - best_pages = i; - } - } - PA_DCHECK(best_pages > 0); - PA_CHECK(best_pages <= MaxSystemPagesPerRegularSlotSpan()); - return static_cast<uint8_t>(best_pages); -} - -} // namespace - -uint8_t ComputeSystemPagesPerSlotSpan(size_t slot_size, - bool prefer_smaller_slot_spans) { - if (prefer_smaller_slot_spans) { - size_t system_page_count = - ComputeSystemPagesPerSlotSpanPreferSmall(slot_size); - size_t waste = (system_page_count * SystemPageSize()) % slot_size; - // In case the waste is too large (more than 5% of a page), don't try to use - // the "small" slot span formula. This happens when we have a lot of - // buckets, in some cases the formula doesn't find a nice, small size. - if (waste <= .05 * SystemPageSize()) { - return system_page_count; - } - } - - return ComputeSystemPagesPerSlotSpanInternal(slot_size); -} - -template <bool thread_safe> -void PartitionBucket<thread_safe>::Init(uint32_t new_slot_size) { - slot_size = new_slot_size; - slot_size_reciprocal = kReciprocalMask / new_slot_size + 1; - active_slot_spans_head = - SlotSpanMetadata<thread_safe>::get_sentinel_slot_span_non_const(); - empty_slot_spans_head = nullptr; - decommitted_slot_spans_head = nullptr; - num_full_slot_spans = 0; - bool prefer_smaller_slot_spans = -#if PA_CONFIG(PREFER_SMALLER_SLOT_SPANS) - true -#else - false -#endif - ; - num_system_pages_per_slot_span = - ComputeSystemPagesPerSlotSpan(slot_size, prefer_smaller_slot_spans); -} - -template <bool thread_safe> -PA_ALWAYS_INLINE SlotSpanMetadata<thread_safe>* -PartitionBucket<thread_safe>::AllocNewSlotSpan(PartitionRoot<thread_safe>* root, - unsigned int flags, - size_t slot_span_alignment) { - PA_DCHECK(!(root->next_partition_page % PartitionPageSize())); - PA_DCHECK(!(root->next_partition_page_end % PartitionPageSize())); - - size_t num_partition_pages = get_pages_per_slot_span(); - size_t slot_span_reservation_size = num_partition_pages - << PartitionPageShift(); - size_t slot_span_committed_size = get_bytes_per_span(); - PA_DCHECK(num_partition_pages <= NumPartitionPagesPerSuperPage()); - PA_DCHECK(slot_span_committed_size % SystemPageSize() == 0); - PA_DCHECK(slot_span_committed_size <= slot_span_reservation_size); - - uintptr_t adjusted_next_partition_page = - base::bits::AlignUp(root->next_partition_page, slot_span_alignment); - if (PA_UNLIKELY(adjusted_next_partition_page + slot_span_reservation_size > - root->next_partition_page_end)) { - // AllocNewSuperPage() may crash (e.g. address space exhaustion), put data - // on stack. - PA_DEBUG_DATA_ON_STACK("slotsize", slot_size); - PA_DEBUG_DATA_ON_STACK("spansize", slot_span_reservation_size); - - // In this case, we can no longer hand out pages from the current super page - // allocation. Get a new super page. - if (!AllocNewSuperPage(root, flags)) { - return nullptr; - } - // AllocNewSuperPage() updates root->next_partition_page, re-query. - adjusted_next_partition_page = - base::bits::AlignUp(root->next_partition_page, slot_span_alignment); - PA_CHECK(adjusted_next_partition_page + slot_span_reservation_size <= - root->next_partition_page_end); - } - - auto* gap_start_page = - PartitionPage<thread_safe>::FromAddr(root->next_partition_page); - auto* gap_end_page = - PartitionPage<thread_safe>::FromAddr(adjusted_next_partition_page); - for (auto* page = gap_start_page; page < gap_end_page; ++page) { - PA_DCHECK(!page->is_valid); - page->has_valid_span_after_this = 1; - } - root->next_partition_page = - adjusted_next_partition_page + slot_span_reservation_size; - - uintptr_t slot_span_start = adjusted_next_partition_page; - auto* slot_span = &gap_end_page->slot_span_metadata; - InitializeSlotSpan(slot_span); - // Now that slot span is initialized, it's safe to call FromSlotStart. - PA_DCHECK(slot_span == - SlotSpanMetadata<thread_safe>::FromSlotStart(slot_span_start)); - - // System pages in the super page come in a decommited state. Commit them - // before vending them back. - // If lazy commit is enabled, pages will be committed when provisioning slots, - // in ProvisionMoreSlotsAndAllocOne(), not here. - if (!kUseLazyCommit) { - PA_DEBUG_DATA_ON_STACK("slotsize", slot_size); - PA_DEBUG_DATA_ON_STACK("spansize", slot_span_reservation_size); - PA_DEBUG_DATA_ON_STACK("spancmt", slot_span_committed_size); - - root->RecommitSystemPagesForData( - slot_span_start, slot_span_committed_size, - PageAccessibilityDisposition::kRequireUpdate); - } - - PA_CHECK(get_slots_per_span() <= - SlotSpanMetadata<ThreadSafe>::kMaxSlotsPerSlotSpan); - - // Double check that we had enough space in the super page for the new slot - // span. - PA_DCHECK(root->next_partition_page <= root->next_partition_page_end); - - return slot_span; -} - -template <bool thread_safe> -uintptr_t PartitionBucket<thread_safe>::AllocNewSuperPageSpan( - PartitionRoot<thread_safe>* root, - size_t super_page_count, - unsigned int flags) { - PA_CHECK(super_page_count > 0); - PA_CHECK(super_page_count <= - std::numeric_limits<size_t>::max() / kSuperPageSize); - // Need a new super page. We want to allocate super pages in a contiguous - // address region as much as possible. This is important for not causing - // page table bloat and not fragmenting address spaces in 32 bit - // architectures. - uintptr_t requested_address = root->next_super_page; - pool_handle pool = root->ChoosePool(); - uintptr_t super_page_span_start = ReserveMemoryFromPool( - pool, requested_address, super_page_count * kSuperPageSize); - if (PA_UNLIKELY(!super_page_span_start)) { - if (flags & AllocFlags::kReturnNull) { - return 0; - } - - // Didn't manage to get a new uncommitted super page -> address space issue. - ::partition_alloc::internal::ScopedUnlockGuard unlock{root->lock_}; - PartitionOutOfMemoryMappingFailure(root, kSuperPageSize); - } - - uintptr_t super_page_span_end = - super_page_span_start + super_page_count * kSuperPageSize; - for (uintptr_t super_page = super_page_span_start; - super_page < super_page_span_end; super_page += kSuperPageSize) { - InitializeSuperPage(root, super_page, 0); - } - return super_page_span_start; -} - -template <bool thread_safe> -PA_ALWAYS_INLINE uintptr_t PartitionBucket<thread_safe>::AllocNewSuperPage( - PartitionRoot<thread_safe>* root, - unsigned int flags) { - auto super_page = AllocNewSuperPageSpan(root, 1, flags); - if (PA_UNLIKELY(!super_page)) { - // If the `kReturnNull` flag isn't set and the allocation attempt fails, - // `AllocNewSuperPageSpan` should've failed with an OOM crash. - PA_DCHECK(flags & AllocFlags::kReturnNull); - return 0; - } - return SuperPagePayloadBegin(super_page, root->IsQuarantineAllowed()); -} - -template <bool thread_safe> -PA_ALWAYS_INLINE uintptr_t PartitionBucket<thread_safe>::InitializeSuperPage( - PartitionRoot<thread_safe>* root, - uintptr_t super_page, - uintptr_t requested_address) { - *ReservationOffsetPointer(super_page) = kOffsetTagNormalBuckets; - - root->total_size_of_super_pages.fetch_add(kSuperPageSize, - std::memory_order_relaxed); - - root->next_super_page = super_page + kSuperPageSize; - uintptr_t state_bitmap = - super_page + PartitionPageSize() + - (is_direct_mapped() ? 0 : ReservedFreeSlotBitmapSize()); -#if BUILDFLAG(USE_STARSCAN) - PA_DCHECK(SuperPageStateBitmapAddr(super_page) == state_bitmap); - const size_t state_bitmap_reservation_size = - root->IsQuarantineAllowed() ? ReservedStateBitmapSize() : 0; - const size_t state_bitmap_size_to_commit = - root->IsQuarantineAllowed() ? CommittedStateBitmapSize() : 0; - PA_DCHECK(state_bitmap_reservation_size % PartitionPageSize() == 0); - PA_DCHECK(state_bitmap_size_to_commit % SystemPageSize() == 0); - PA_DCHECK(state_bitmap_size_to_commit <= state_bitmap_reservation_size); - uintptr_t payload = state_bitmap + state_bitmap_reservation_size; -#else - uintptr_t payload = state_bitmap; -#endif // BUILDFLAG(USE_STARSCAN) - - root->next_partition_page = payload; - root->next_partition_page_end = root->next_super_page - PartitionPageSize(); - PA_DCHECK(payload == - SuperPagePayloadBegin(super_page, root->IsQuarantineAllowed())); - PA_DCHECK(root->next_partition_page_end == SuperPagePayloadEnd(super_page)); - - // Keep the first partition page in the super page inaccessible to serve as a - // guard page, except an "island" in the middle where we put page metadata and - // also a tiny amount of extent metadata. - { - ScopedSyscallTimer timer{root}; - RecommitSystemPages(super_page + SystemPageSize(), SystemPageSize(), -#if PA_CONFIG(ENABLE_SHADOW_METADATA) - root->PageAccessibilityWithPkeyIfEnabled( - PageAccessibilityConfiguration::kRead), -#else - root->PageAccessibilityWithPkeyIfEnabled( - PageAccessibilityConfiguration::kReadWrite), -#endif - PageAccessibilityDisposition::kRequireUpdate); - } - -#if BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) - // If PUT_REF_COUNT_IN_PREVIOUS_SLOT is on, and if the BRP pool is - // used, allocate a SystemPage for RefCount bitmap. - if (root->ChoosePool() == kBRPPoolHandle) { - ScopedSyscallTimer timer{root}; - RecommitSystemPages(super_page + SystemPageSize() * 2, SystemPageSize(), - root->PageAccessibilityWithPkeyIfEnabled( - PageAccessibilityConfiguration::kReadWrite), - PageAccessibilityDisposition::kRequireUpdate); - } -#endif - -#if PA_CONFIG(ENABLE_SHADOW_METADATA) - { - ScopedSyscallTimer timer{root}; - RecommitSystemPages(ShadowMetadataStart(super_page, root->ChoosePool()), - SystemPageSize(), - root->PageAccessibilityWithPkeyIfEnabled( - PageAccessibilityConfiguration::kReadWrite), - PageAccessibilityDisposition::kRequireUpdate); - } -#endif - - // If we were after a specific address, but didn't get it, assume that - // the system chose a lousy address. Here most OS'es have a default - // algorithm that isn't randomized. For example, most Linux - // distributions will allocate the mapping directly before the last - // successful mapping, which is far from random. So we just get fresh - // randomness for the next mapping attempt. - if (requested_address && requested_address != super_page) { - root->next_super_page = 0; - } - - // We allocated a new super page so update super page metadata. - // First check if this is a new extent or not. - auto* latest_extent = PartitionSuperPageToExtent<thread_safe>(super_page); - // By storing the root in every extent metadata object, we have a fast way - // to go from a pointer within the partition to the root object. - latest_extent->root = root; - // Most new extents will be part of a larger extent, and these two fields - // are unused, but we initialize them to 0 so that we get a clear signal - // in case they are accidentally used. - latest_extent->number_of_consecutive_super_pages = 0; - latest_extent->next = nullptr; - latest_extent->number_of_nonempty_slot_spans = 0; - - PartitionSuperPageExtentEntry<thread_safe>* current_extent = - root->current_extent; - const bool is_new_extent = super_page != requested_address; - if (PA_UNLIKELY(is_new_extent)) { - if (PA_UNLIKELY(!current_extent)) { - PA_DCHECK(!root->first_extent); - root->first_extent = latest_extent; - } else { - PA_DCHECK(current_extent->number_of_consecutive_super_pages); - current_extent->next = latest_extent; - } - root->current_extent = latest_extent; - latest_extent->number_of_consecutive_super_pages = 1; - } else { - // We allocated next to an existing extent so just nudge the size up a - // little. - PA_DCHECK(current_extent->number_of_consecutive_super_pages); - ++current_extent->number_of_consecutive_super_pages; - PA_DCHECK(payload > SuperPagesBeginFromExtent(current_extent) && - payload < SuperPagesEndFromExtent(current_extent)); - } - - // If PCScan is used, commit the state bitmap. Otherwise, leave it uncommitted - // and let PartitionRoot::RegisterScannableRoot() commit it when needed. Make - // sure to register the super-page after it has been fully initialized. - // Otherwise, the concurrent scanner may try to access |extent->root| which - // could be not initialized yet. -#if BUILDFLAG(USE_STARSCAN) - if (root->IsQuarantineEnabled()) { - { - ScopedSyscallTimer timer{root}; - RecommitSystemPages(state_bitmap, state_bitmap_size_to_commit, - root->PageAccessibilityWithPkeyIfEnabled( - PageAccessibilityConfiguration::kReadWrite), - PageAccessibilityDisposition::kRequireUpdate); - } - PCScan::RegisterNewSuperPage(root, super_page); - } -#endif // BUILDFLAG(USE_STARSCAN) - -#if BUILDFLAG(USE_FREESLOT_BITMAP) - // Commit the pages for freeslot bitmap. - if (!is_direct_mapped()) { - uintptr_t freeslot_bitmap_addr = super_page + PartitionPageSize(); - PA_DCHECK(SuperPageFreeSlotBitmapAddr(super_page) == freeslot_bitmap_addr); - ScopedSyscallTimer timer{root}; - RecommitSystemPages(freeslot_bitmap_addr, CommittedFreeSlotBitmapSize(), - root->PageAccessibilityWithPkeyIfEnabled( - PageAccessibilityConfiguration::kReadWrite), - PageAccessibilityDisposition::kRequireUpdate); - } -#endif - - return payload; -} - -template <bool thread_safe> -PA_ALWAYS_INLINE void PartitionBucket<thread_safe>::InitializeSlotSpan( - SlotSpanMetadata<thread_safe>* slot_span) { - new (slot_span) SlotSpanMetadata<thread_safe>(this); - - slot_span->Reset(); - - uint16_t num_partition_pages = get_pages_per_slot_span(); - auto* page = reinterpret_cast<PartitionPage<thread_safe>*>(slot_span); - for (uint16_t i = 0; i < num_partition_pages; ++i, ++page) { - PA_DCHECK(i <= PartitionPage<thread_safe>::kMaxSlotSpanMetadataOffset); - page->slot_span_metadata_offset = i; - page->is_valid = true; - } -} - -template <bool thread_safe> -PA_ALWAYS_INLINE uintptr_t -PartitionBucket<thread_safe>::ProvisionMoreSlotsAndAllocOne( - PartitionRoot<thread_safe>* root, - SlotSpanMetadata<thread_safe>* slot_span) { - PA_DCHECK(slot_span != - SlotSpanMetadata<thread_safe>::get_sentinel_slot_span()); - size_t num_slots = slot_span->num_unprovisioned_slots; - PA_DCHECK(num_slots); - PA_DCHECK(num_slots <= get_slots_per_span()); - // We should only get here when _every_ slot is either used or unprovisioned. - // (The third possible state is "on the freelist". If we have a non-empty - // freelist, we should not get here.) - PA_DCHECK(num_slots + slot_span->num_allocated_slots == get_slots_per_span()); - // Similarly, make explicitly sure that the freelist is empty. - PA_DCHECK(!slot_span->get_freelist_head()); - PA_DCHECK(!slot_span->is_full()); - - uintptr_t slot_span_start = - SlotSpanMetadata<thread_safe>::ToSlotSpanStart(slot_span); - // If we got here, the first unallocated slot is either partially or fully on - // an uncommitted page. If the latter, it must be at the start of that page. - uintptr_t return_slot = - slot_span_start + (slot_size * slot_span->num_allocated_slots); - uintptr_t next_slot = return_slot + slot_size; - uintptr_t commit_start = base::bits::AlignUp(return_slot, SystemPageSize()); - PA_DCHECK(next_slot > commit_start); - uintptr_t commit_end = base::bits::AlignUp(next_slot, SystemPageSize()); - // If the slot was partially committed, |return_slot| and |next_slot| fall - // in different pages. If the slot was fully uncommitted, |return_slot| points - // to the page start and |next_slot| doesn't, thus only the latter gets - // rounded up. - PA_DCHECK(commit_end > commit_start); - - // The slot being returned is considered allocated. - slot_span->num_allocated_slots++; - // Round down, because a slot that doesn't fully fit in the new page(s) isn't - // provisioned. - size_t slots_to_provision = (commit_end - return_slot) / slot_size; - slot_span->num_unprovisioned_slots -= slots_to_provision; - PA_DCHECK(slot_span->num_allocated_slots + - slot_span->num_unprovisioned_slots <= - get_slots_per_span()); - - // If lazy commit is enabled, meaning system pages in the slot span come - // in an initially decommitted state, commit them here. - // Note, we can't use PageAccessibilityDisposition::kAllowKeepForPerf, because - // we have no knowledge which pages have been committed before (it doesn't - // matter on Windows anyway). - if (kUseLazyCommit) { - // TODO(lizeb): Handle commit failure. - root->RecommitSystemPagesForData( - commit_start, commit_end - commit_start, - PageAccessibilityDisposition::kRequireUpdate); - } - - if (PA_LIKELY(slot_size <= kMaxMemoryTaggingSize && - root->IsMemoryTaggingEnabled())) { - // Ensure the MTE-tag of the memory pointed by |return_slot| is unguessable. - TagMemoryRangeRandomly(return_slot, slot_size); - } - - // Add all slots that fit within so far committed pages to the free list. - PartitionFreelistEntry* prev_entry = nullptr; - uintptr_t next_slot_end = next_slot + slot_size; - size_t free_list_entries_added = 0; - while (next_slot_end <= commit_end) { - void* next_slot_ptr; - if (PA_LIKELY(slot_size <= kMaxMemoryTaggingSize)) { - // Ensure the MTE-tag of the memory pointed by other provisioned slot is - // unguessable. They will be returned to the app as is, and the MTE-tag - // will only change upon calling Free(). - next_slot_ptr = TagMemoryRangeRandomly(next_slot, slot_size); - } else { - // No MTE-tagging for larger slots, just cast. - next_slot_ptr = reinterpret_cast<void*>(next_slot); - } - auto* entry = PartitionFreelistEntry::EmplaceAndInitNull(next_slot_ptr); - if (!slot_span->get_freelist_head()) { - PA_DCHECK(!prev_entry); - PA_DCHECK(!free_list_entries_added); - slot_span->SetFreelistHead(entry); - } else { - PA_DCHECK(free_list_entries_added); - prev_entry->SetNext(entry); - } -#if BUILDFLAG(USE_FREESLOT_BITMAP) - FreeSlotBitmapMarkSlotAsFree(next_slot); -#endif - next_slot = next_slot_end; - next_slot_end = next_slot + slot_size; - prev_entry = entry; -#if BUILDFLAG(PA_DCHECK_IS_ON) - free_list_entries_added++; -#endif - } - -#if BUILDFLAG(USE_FREESLOT_BITMAP) - FreeSlotBitmapMarkSlotAsFree(return_slot); -#endif - -#if BUILDFLAG(PA_DCHECK_IS_ON) - // The only provisioned slot not added to the free list is the one being - // returned. - PA_DCHECK(slots_to_provision == free_list_entries_added + 1); - // We didn't necessarily provision more than one slot (e.g. if |slot_size| - // is large), meaning that |slot_span->freelist_head| can be nullptr. - if (slot_span->get_freelist_head()) { - PA_DCHECK(free_list_entries_added); - slot_span->get_freelist_head()->CheckFreeList(slot_size); - } -#endif - - // We had no free slots, and created some (potentially 0) in sorted order. - slot_span->set_freelist_sorted(); - - return return_slot; -} - -template <bool thread_safe> -bool PartitionBucket<thread_safe>::SetNewActiveSlotSpan() { - SlotSpanMetadata<thread_safe>* slot_span = active_slot_spans_head; - if (slot_span == SlotSpanMetadata<thread_safe>::get_sentinel_slot_span()) { - return false; - } - - SlotSpanMetadata<thread_safe>* next_slot_span; - - // The goal here is to find a suitable slot span in the active list. Suitable - // slot spans are |is_active()|, i.e. they either have (a) freelist entries, - // or (b) unprovisioned free space. The first case is preferable, since it - // doesn't cost a system call, and doesn't cause new memory to become dirty. - // - // While looking for a new slot span, active list maintenance is performed, - // that is: - // - Empty and decommitted slot spans are moved to their respective lists. - // - Full slot spans are removed from the active list but are not moved - // anywhere. They could be tracked in a separate list, but this would - // increase cost non trivially. Indeed, a full slot span is likely to become - // non-full at some point (due to a free() hitting it). Since we only have - // space in the metadata for a single linked list pointer, removing the - // newly-non-full slot span from the "full" list would require walking it - // (to know what's before it in the full list). - // - // Since we prefer slot spans with provisioned freelist entries, maintenance - // happens in two stages: - // 1. Walk the list to find candidates. Each of the skipped slot span is moved - // to either: - // - one of the long-lived lists: empty, decommitted - // - the temporary "active slots spans with no freelist entry" list - // - Nowhere for full slot spans. - // 2. Once we have a candidate: - // - Set it as the new active list head - // - Reattach the temporary list - // - // Note that in most cases, the whole list will not be walked and maintained - // at this stage. - - SlotSpanMetadata<thread_safe>* to_provision_head = nullptr; - SlotSpanMetadata<thread_safe>* to_provision_tail = nullptr; - - for (; slot_span; slot_span = next_slot_span) { - next_slot_span = slot_span->next_slot_span; - PA_DCHECK(slot_span->bucket == this); - PA_DCHECK(slot_span != empty_slot_spans_head); - PA_DCHECK(slot_span != decommitted_slot_spans_head); - - if (slot_span->is_active()) { - // Has provisioned slots. - if (slot_span->get_freelist_head()) { - // Will use this slot span, no need to go further. - break; - } else { - // Keeping head and tail because we don't want to reverse the list. - if (!to_provision_head) { - to_provision_head = slot_span; - } - if (to_provision_tail) { - to_provision_tail->next_slot_span = slot_span; - } - to_provision_tail = slot_span; - slot_span->next_slot_span = nullptr; - } - } else if (slot_span->is_empty()) { - slot_span->next_slot_span = empty_slot_spans_head; - empty_slot_spans_head = slot_span; - } else if (PA_LIKELY(slot_span->is_decommitted())) { - slot_span->next_slot_span = decommitted_slot_spans_head; - decommitted_slot_spans_head = slot_span; - } else { - PA_DCHECK(slot_span->is_full()); - // Move this slot span... nowhere, and also mark it as full. We need it - // marked so that free'ing can tell, and move it back into the active - // list. - slot_span->marked_full = 1; - ++num_full_slot_spans; - // Overflow. Most likely a correctness issue in the code. It is in theory - // possible that the number of full slot spans really reaches (1 << 24), - // but this is very unlikely (and not possible with most pool settings). - PA_CHECK(num_full_slot_spans); - // Not necessary but might help stop accidents. - slot_span->next_slot_span = nullptr; - } - } - - bool usable_active_list_head = false; - // Found an active slot span with provisioned entries on the freelist. - if (slot_span) { - usable_active_list_head = true; - // We have active slot spans with unprovisioned entries. Re-attach them into - // the active list, past the span with freelist entries. - if (to_provision_head) { - auto* next = slot_span->next_slot_span; - slot_span->next_slot_span = to_provision_head; - to_provision_tail->next_slot_span = next; - } - active_slot_spans_head = slot_span; - } else if (to_provision_head) { - usable_active_list_head = true; - // Need to provision new slots. - active_slot_spans_head = to_provision_head; - } else { - // Active list is now empty. - active_slot_spans_head = - SlotSpanMetadata<thread_safe>::get_sentinel_slot_span_non_const(); - } - - return usable_active_list_head; -} - -template <bool thread_safe> -void PartitionBucket<thread_safe>::MaintainActiveList() { - SlotSpanMetadata<thread_safe>* slot_span = active_slot_spans_head; - if (slot_span == SlotSpanMetadata<thread_safe>::get_sentinel_slot_span()) { - return; - } - - SlotSpanMetadata<thread_safe>* new_active_slot_spans_head = nullptr; - SlotSpanMetadata<thread_safe>* new_active_slot_spans_tail = nullptr; - - SlotSpanMetadata<thread_safe>* next_slot_span; - for (; slot_span; slot_span = next_slot_span) { - next_slot_span = slot_span->next_slot_span; - - if (slot_span->is_active()) { - // Ordering in the active slot span list matters, don't reverse it. - if (!new_active_slot_spans_head) { - new_active_slot_spans_head = slot_span; - } - if (new_active_slot_spans_tail) { - new_active_slot_spans_tail->next_slot_span = slot_span; - } - new_active_slot_spans_tail = slot_span; - slot_span->next_slot_span = nullptr; - } else if (slot_span->is_empty()) { - // For the empty and decommitted lists, LIFO ordering makes sense (since - // it would lead to reusing memory which has been touched relatively - // recently, which only matters for committed spans though). - slot_span->next_slot_span = empty_slot_spans_head; - empty_slot_spans_head = slot_span; - } else if (slot_span->is_decommitted()) { - slot_span->next_slot_span = decommitted_slot_spans_head; - decommitted_slot_spans_head = slot_span; - } else { - // Full slot spans are not tracked, just accounted for. - PA_DCHECK(slot_span->is_full()); - slot_span->marked_full = 1; - ++num_full_slot_spans; - PA_CHECK(num_full_slot_spans); // Overflow. - slot_span->next_slot_span = nullptr; - } - } - - if (!new_active_slot_spans_head) { - new_active_slot_spans_head = - SlotSpanMetadata<thread_safe>::get_sentinel_slot_span_non_const(); - } - active_slot_spans_head = new_active_slot_spans_head; -} - -template <bool thread_safe> -void PartitionBucket<thread_safe>::SortSlotSpanFreelists() { - for (auto* slot_span = active_slot_spans_head; slot_span; - slot_span = slot_span->next_slot_span) { - // No need to sort the freelist if it's already sorted. Note that if the - // freelist is sorted, this means that it didn't change at all since the - // last call. This may be a good signal to shrink it if possible (if an - // entire OS page is free, we can decommit it). - // - // Besides saving CPU, this also avoids touching memory of fully idle slot - // spans, which may required paging. - if (slot_span->num_allocated_slots > 0 && - !slot_span->freelist_is_sorted()) { - slot_span->SortFreelist(); - } - } -} - -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -bool CompareSlotSpans(SlotSpanMetadata<ThreadSafe>* a, - SlotSpanMetadata<ThreadSafe>* b) { - auto criteria_tuple = [](SlotSpanMetadata<ThreadSafe> const* a) { - size_t freelist_length = a->GetFreelistLength(); - // The criteria are, in order (hence the lexicographic comparison below): - // 1. Prefer slot spans with freelist entries. The ones without freelist - // entries would be skipped in SetNewActiveSlotSpan() anyway. - // 2. Then the ones with the fewest freelist entries. They are either close - // to being full (for the provisioned memory), or close to being pushed - // at the end of the list (since they would not have freelist entries - // anymore, and would either fall into the first case, or be skipped by - // SetNewActiveSlotSpan()). - // 3. The ones with the fewer unprovisioned slots, meaning that they are - // close to being completely full. - // - // Note that this sorting order is not necessarily the best one when slot - // spans are partially provisioned. From local testing, in steady-state, - // most slot spans are entirely provisioned (or decommitted), which may be a - // consequence of the lack of partial slot span decommit, or of fairly - // effective fragmentation avoidance heuristics. Make sure to evaluate - // whether an alternative sorting order (sorting according to freelist size - // + unprovisioned slots) makes more sense. - return std::tuple<bool, size_t, size_t>{ - freelist_length == 0, freelist_length, a->num_unprovisioned_slots}; - }; - - return criteria_tuple(a) < criteria_tuple(b); -} - -template <bool thread_safe> -void PartitionBucket<thread_safe>::SortActiveSlotSpans() { - // Sorting up to |kMaxSlotSpansToSort| slot spans. This is capped for two - // reasons: - // - Limiting execution time - // - Current code cannot allocate. - // - // In practice though, it's rare to have that many active slot spans. - SlotSpanMetadata<thread_safe>* active_spans_array[kMaxSlotSpansToSort]; - size_t index = 0; - SlotSpanMetadata<thread_safe>* overflow_spans_start = nullptr; - - for (auto* slot_span = active_slot_spans_head; slot_span; - slot_span = slot_span->next_slot_span) { - if (index < kMaxSlotSpansToSort) { - active_spans_array[index++] = slot_span; - } else { - // Starting from this one, not sorting the slot spans. - overflow_spans_start = slot_span; - break; - } - } - - // We sort the active slot spans so that allocations are preferably serviced - // from the fullest ones. This way we hope to reduce fragmentation by keeping - // as few slot spans as full as possible. - // - // With perfect information on allocation lifespan, we would be able to pack - // allocations and get almost no fragmentation. This is obviously not the - // case, so we have partially full SlotSpans. Nevertheless, as a heuristic we - // want to: - // - Keep almost-empty slot spans as empty as possible - // - Keep mostly-full slot spans as full as possible - // - // The first part is done in the hope that future free()s will make these - // slot spans completely empty, allowing us to reclaim them. To that end, sort - // SlotSpans periodically so that the fullest ones are preferred. - // - // std::sort() is not completely guaranteed to never allocate memory. However, - // it may not throw std::bad_alloc, which constrains the implementation. In - // addition, this is protected by the reentrancy guard, so we would detect - // such an allocation. - std::sort(active_spans_array, active_spans_array + index, CompareSlotSpans); - - active_slot_spans_head = overflow_spans_start; - - // Reverse order, since we insert at the head of the list. - for (int i = index - 1; i >= 0; i--) { - if (active_spans_array[i] == - SlotSpanMetadata<thread_safe>::get_sentinel_slot_span()) { - // The sentinel is const, don't try to write to it. - PA_DCHECK(active_slot_spans_head == nullptr); - } else { - active_spans_array[i]->next_slot_span = active_slot_spans_head; - } - active_slot_spans_head = active_spans_array[i]; - } -} - -template <bool thread_safe> -uintptr_t PartitionBucket<thread_safe>::SlowPathAlloc( - PartitionRoot<thread_safe>* root, - unsigned int flags, - size_t raw_size, - size_t slot_span_alignment, - bool* is_already_zeroed) { - PA_DCHECK((slot_span_alignment >= PartitionPageSize()) && - base::bits::IsPowerOfTwo(slot_span_alignment)); - - // The slow path is called when the freelist is empty. The only exception is - // when a higher-order alignment is requested, in which case the freelist - // logic is bypassed and we go directly for slot span allocation. - bool allocate_aligned_slot_span = slot_span_alignment > PartitionPageSize(); - PA_DCHECK(!active_slot_spans_head->get_freelist_head() || - allocate_aligned_slot_span); - - SlotSpanMetadata<thread_safe>* new_slot_span = nullptr; - // |new_slot_span->bucket| will always be |this|, except when |this| is the - // sentinel bucket, which is used to signal a direct mapped allocation. In - // this case |new_bucket| will be set properly later. This avoids a read for - // most allocations. - PartitionBucket* new_bucket = this; - *is_already_zeroed = false; - - // For the PartitionRoot::Alloc() API, we have a bunch of buckets - // marked as special cases. We bounce them through to the slow path so that - // we can still have a blazing fast hot path due to lack of corner-case - // branches. - // - // Note: The ordering of the conditionals matter! In particular, - // SetNewActiveSlotSpan() has a side-effect even when returning - // false where it sweeps the active list and may move things into the empty or - // decommitted lists which affects the subsequent conditional. - if (PA_UNLIKELY(is_direct_mapped())) { - PA_DCHECK(raw_size > kMaxBucketed); - PA_DCHECK(this == &root->sentinel_bucket); - PA_DCHECK(active_slot_spans_head == - SlotSpanMetadata<thread_safe>::get_sentinel_slot_span()); - - // No fast path for direct-mapped allocations. - if (flags & AllocFlags::kFastPathOrReturnNull) { - return 0; - } - - new_slot_span = - PartitionDirectMap(root, flags, raw_size, slot_span_alignment); - if (new_slot_span) { - new_bucket = new_slot_span->bucket; - } - // Memory from PageAllocator is always zeroed. - *is_already_zeroed = true; - } else if (PA_LIKELY(!allocate_aligned_slot_span && SetNewActiveSlotSpan())) { - // First, did we find an active slot span in the active list? - new_slot_span = active_slot_spans_head; - PA_DCHECK(new_slot_span->is_active()); - } else if (PA_LIKELY(!allocate_aligned_slot_span && - (empty_slot_spans_head != nullptr || - decommitted_slot_spans_head != nullptr))) { - // Second, look in our lists of empty and decommitted slot spans. - // Check empty slot spans first, which are preferred, but beware that an - // empty slot span might have been decommitted. - while (PA_LIKELY((new_slot_span = empty_slot_spans_head) != nullptr)) { - PA_DCHECK(new_slot_span->bucket == this); - PA_DCHECK(new_slot_span->is_empty() || new_slot_span->is_decommitted()); - empty_slot_spans_head = new_slot_span->next_slot_span; - // Accept the empty slot span unless it got decommitted. - if (new_slot_span->get_freelist_head()) { - new_slot_span->next_slot_span = nullptr; - new_slot_span->ToSuperPageExtent() - ->IncrementNumberOfNonemptySlotSpans(); - - // Re-activating an empty slot span, update accounting. - size_t dirty_size = base::bits::AlignUp( - new_slot_span->GetProvisionedSize(), SystemPageSize()); - PA_DCHECK(root->empty_slot_spans_dirty_bytes >= dirty_size); - root->empty_slot_spans_dirty_bytes -= dirty_size; - - break; - } - PA_DCHECK(new_slot_span->is_decommitted()); - new_slot_span->next_slot_span = decommitted_slot_spans_head; - decommitted_slot_spans_head = new_slot_span; - } - if (PA_UNLIKELY(!new_slot_span) && - PA_LIKELY(decommitted_slot_spans_head != nullptr)) { - // Commit can be expensive, don't do it. - if (flags & AllocFlags::kFastPathOrReturnNull) { - return 0; - } - - new_slot_span = decommitted_slot_spans_head; - PA_DCHECK(new_slot_span->bucket == this); - PA_DCHECK(new_slot_span->is_decommitted()); - decommitted_slot_spans_head = new_slot_span->next_slot_span; - - // If lazy commit is enabled, pages will be recommitted when provisioning - // slots, in ProvisionMoreSlotsAndAllocOne(), not here. - if (!kUseLazyCommit) { - uintptr_t slot_span_start = - SlotSpanMetadata<thread_safe>::ToSlotSpanStart(new_slot_span); - // Since lazy commit isn't used, we have a guarantee that all slot span - // pages have been previously committed, and then decommitted using - // PageAccessibilityDisposition::kAllowKeepForPerf, so use the - // same option as an optimization. - // TODO(lizeb): Handle commit failure. - root->RecommitSystemPagesForData( - slot_span_start, new_slot_span->bucket->get_bytes_per_span(), - PageAccessibilityDisposition::kAllowKeepForPerf); - } - - new_slot_span->Reset(); - *is_already_zeroed = DecommittedMemoryIsAlwaysZeroed(); - } - PA_DCHECK(new_slot_span); - } else { - // Getting a new slot span is expensive, don't do it. - if (flags & AllocFlags::kFastPathOrReturnNull) { - return 0; - } - - // Third. If we get here, we need a brand new slot span. - // TODO(bartekn): For single-slot slot spans, we can use rounded raw_size - // as slot_span_committed_size. - new_slot_span = AllocNewSlotSpan(root, flags, slot_span_alignment); - // New memory from PageAllocator is always zeroed. - *is_already_zeroed = true; - } - - // Bail if we had a memory allocation failure. - if (PA_UNLIKELY(!new_slot_span)) { - PA_DCHECK(active_slot_spans_head == - SlotSpanMetadata<thread_safe>::get_sentinel_slot_span()); - if (flags & AllocFlags::kReturnNull) { - return 0; - } - // See comment in PartitionDirectMap() for unlocking. - ScopedUnlockGuard unlock{root->lock_}; - root->OutOfMemory(raw_size); - PA_IMMEDIATE_CRASH(); // Not required, kept as documentation. - } - - PA_DCHECK(new_bucket != &root->sentinel_bucket); - new_bucket->active_slot_spans_head = new_slot_span; - if (new_slot_span->CanStoreRawSize()) { - new_slot_span->SetRawSize(raw_size); - } - - // If we found an active slot span with free slots, or an empty slot span, we - // have a usable freelist head. - if (PA_LIKELY(new_slot_span->get_freelist_head() != nullptr)) { - PartitionFreelistEntry* entry = - new_slot_span->PopForAlloc(new_bucket->slot_size); - - // We may have set *is_already_zeroed to true above, make sure that the - // freelist entry doesn't contain data. Either way, it wouldn't be a good - // idea to let users see our internal data. - uintptr_t slot_start = entry->ClearForAllocation(); - return slot_start; - } - - // Otherwise, we need to provision more slots by committing more pages. Build - // the free list for the newly provisioned slots. - PA_DCHECK(new_slot_span->num_unprovisioned_slots); - return ProvisionMoreSlotsAndAllocOne(root, new_slot_span); -} - -template <bool thread_safe> -uintptr_t PartitionBucket<thread_safe>::AllocNewSuperPageSpanForGwpAsan( - PartitionRoot<thread_safe>* root, - size_t super_page_count, - unsigned int flags) { - return AllocNewSuperPageSpan(root, super_page_count, flags); -} - -template <bool thread_safe> -void PartitionBucket<thread_safe>::InitializeSlotSpanForGwpAsan( - SlotSpanMetadata<thread_safe>* slot_span) { - InitializeSlotSpan(slot_span); -} - -template struct PartitionBucket<ThreadSafe>; - -} // namespace partition_alloc::internal
diff --git a/base/allocator/partition_allocator/partition_bucket.h b/base/allocator/partition_allocator/partition_bucket.h deleted file mode 100644 index eda607b..0000000 --- a/base/allocator/partition_allocator/partition_bucket.h +++ /dev/null
@@ -1,229 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_BUCKET_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_BUCKET_H_ - -#include <cstddef> -#include <cstdint> - -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_base/thread_annotations.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "base/allocator/partition_allocator/partition_alloc_forward.h" - -namespace partition_alloc::internal { - -constexpr inline int kPartitionNumSystemPagesPerSlotSpanBits = 8; - -// Visible for testing. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -uint8_t ComputeSystemPagesPerSlotSpan(size_t slot_size, - bool prefer_smaller_slot_spans); - -// Visible for testing. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -bool CompareSlotSpans(SlotSpanMetadata<ThreadSafe>* a, - SlotSpanMetadata<ThreadSafe>* b); - -template <bool thread_safe> -struct PartitionBucket { - // Accessed most in hot path => goes first. Only nullptr for invalid buckets, - // may be pointing to the sentinel. - SlotSpanMetadata<thread_safe>* active_slot_spans_head; - - SlotSpanMetadata<thread_safe>* empty_slot_spans_head; - SlotSpanMetadata<thread_safe>* decommitted_slot_spans_head; - uint32_t slot_size; - uint32_t num_system_pages_per_slot_span - : kPartitionNumSystemPagesPerSlotSpanBits; - uint32_t num_full_slot_spans : 24; - - // `slot_size_reciprocal` is used to improve the performance of - // `GetSlotOffset`. It is computed as `(1 / size) * (2 ** M)` where M is - // chosen to provide the desired accuracy. As a result, we can replace a slow - // integer division (or modulo) operation with a pair of multiplication and a - // bit shift, i.e. `value / size` becomes `(value * size_reciprocal) >> M`. - uint64_t slot_size_reciprocal; - - // This is `M` from the formula above. For accurate results, both `value` and - // `size`, which are bound by `kMaxBucketed` for our purposes, must be less - // than `2 ** (M / 2)`. On the other hand, the result of the expression - // `3 * M / 2` must be less than 64, otherwise integer overflow can occur. - static constexpr uint64_t kReciprocalShift = 42; - static constexpr uint64_t kReciprocalMask = (1ull << kReciprocalShift) - 1; - static_assert( - kMaxBucketed < (1 << (kReciprocalShift / 2)), - "GetSlotOffset may produce an incorrect result when kMaxBucketed is too " - "large."); - - static constexpr size_t kMaxSlotSpansToSort = 200; - - // Public API. - PA_COMPONENT_EXPORT(PARTITION_ALLOC) void Init(uint32_t new_slot_size); - - // Sets |is_already_zeroed| to true if the allocation was satisfied by - // requesting (a) new page(s) from the operating system, or false otherwise. - // This enables an optimization for when callers use - // |AllocFlags::kZeroFill|: there is no need to call memset on fresh - // pages; the OS has already zeroed them. (See - // |PartitionRoot::AllocFromBucket|.) - // - // Note the matching Free() functions are in SlotSpanMetadata. - PA_NOINLINE PA_COMPONENT_EXPORT(PARTITION_ALLOC) uintptr_t - SlowPathAlloc(PartitionRoot<thread_safe>* root, - unsigned int flags, - size_t raw_size, - size_t slot_span_alignment, - bool* is_already_zeroed) - PA_EXCLUSIVE_LOCKS_REQUIRED(root->lock_); - - PA_ALWAYS_INLINE bool CanStoreRawSize() const { - // For direct-map as well as single-slot slot spans (recognized by checking - // against |MaxRegularSlotSpanSize()|), we have some spare metadata space in - // subsequent PartitionPage to store the raw size. It isn't only metadata - // space though, slot spans that have more than one slot can't have raw size - // stored, because we wouldn't know which slot it applies to. - if (PA_LIKELY(slot_size <= MaxRegularSlotSpanSize())) { - return false; - } - - PA_DCHECK((slot_size % SystemPageSize()) == 0); - PA_DCHECK(is_direct_mapped() || get_slots_per_span() == 1); - - return true; - } - - // Some buckets are pseudo-buckets, which are disabled because they would - // otherwise not fulfill alignment constraints. - PA_ALWAYS_INLINE bool is_valid() const { - return active_slot_spans_head != nullptr; - } - PA_ALWAYS_INLINE bool is_direct_mapped() const { - return !num_system_pages_per_slot_span; - } - PA_ALWAYS_INLINE size_t get_bytes_per_span() const { - // Cannot overflow, num_system_pages_per_slot_span is a bitfield, and 255 - // pages fit in a size_t. - static_assert(kPartitionNumSystemPagesPerSlotSpanBits <= 8, ""); - return static_cast<size_t>(num_system_pages_per_slot_span) - << SystemPageShift(); - } - PA_ALWAYS_INLINE size_t get_slots_per_span() const { - size_t ret = GetSlotNumber(get_bytes_per_span()); - PA_DCHECK(ret <= SlotSpanMetadata<thread_safe>::kMaxSlotsPerSlotSpan); - return ret; - } - // Returns a natural number of partition pages (calculated by - // ComputeSystemPagesPerSlotSpan()) to allocate from the current super page - // when the bucket runs out of slots. - PA_ALWAYS_INLINE size_t get_pages_per_slot_span() const { - // Rounds up to nearest multiple of NumSystemPagesPerPartitionPage(). - return (num_system_pages_per_slot_span + - (NumSystemPagesPerPartitionPage() - 1)) / - NumSystemPagesPerPartitionPage(); - } - - // This helper function scans a bucket's active slot span list for a suitable - // new active slot span. When it finds a suitable new active slot span (one - // that has free slots and is not empty), it is set as the new active slot - // span. If there is no suitable new active slot span, the current active slot - // span is set to SlotSpanMetadata::get_sentinel_slot_span(). As potential - // slot spans are scanned, they are tidied up according to their state. Empty - // slot spans are swept on to the empty list, decommitted slot spans on to the - // decommitted list and full slot spans are unlinked from any list. - // - // This is where the guts of the bucket maintenance is done! - bool SetNewActiveSlotSpan(); - - // Walks the entire active slot span list, and perform regular maintenance, - // where empty, decommitted and full slot spans are moved to their - // steady-state place. - PA_COMPONENT_EXPORT(PARTITION_ALLOC) void MaintainActiveList(); - - // Returns a slot number starting from the beginning of the slot span. - PA_ALWAYS_INLINE size_t GetSlotNumber(size_t offset_in_slot_span) const { - // See the static assertion for `kReciprocalShift` above. - PA_DCHECK(offset_in_slot_span <= kMaxBucketed); - PA_DCHECK(slot_size <= kMaxBucketed); - - const size_t offset_in_slot = - ((offset_in_slot_span * slot_size_reciprocal) >> kReciprocalShift); - PA_DCHECK(offset_in_slot_span / slot_size == offset_in_slot); - - return offset_in_slot; - } - - // Sort the freelists of all slot spans. - void SortSlotSpanFreelists(); - // Sort the active slot span list in ascending freelist length. - PA_COMPONENT_EXPORT(PARTITION_ALLOC) void SortActiveSlotSpans(); - - // We need `AllocNewSuperPageSpan` and `InitializeSlotSpan` to stay - // PA_ALWAYS_INLINE for speed, but we also need to use them from a separate - // compilation unit. - uintptr_t AllocNewSuperPageSpanForGwpAsan(PartitionRoot<thread_safe>* root, - size_t super_page_count, - unsigned int flags) - PA_EXCLUSIVE_LOCKS_REQUIRED(root->lock_); - void InitializeSlotSpanForGwpAsan(SlotSpanMetadata<thread_safe>* slot_span); - - private: - // Allocates several consecutive super pages. Returns the address of the first - // super page. - PA_ALWAYS_INLINE uintptr_t AllocNewSuperPageSpan( - PartitionRoot<thread_safe>* root, - size_t super_page_count, - unsigned int flags) PA_EXCLUSIVE_LOCKS_REQUIRED(root->lock_); - - // Allocates a new slot span with size |num_partition_pages| from the - // current extent. Metadata within this slot span will be initialized. - // Returns nullptr on error. - PA_ALWAYS_INLINE SlotSpanMetadata<thread_safe>* AllocNewSlotSpan( - PartitionRoot<thread_safe>* root, - unsigned int flags, - size_t slot_span_alignment) PA_EXCLUSIVE_LOCKS_REQUIRED(root->lock_); - - // Allocates a new super page from the current extent, if possible. All - // slot-spans will be in the decommitted state. Returns the address of the - // super page's payload, or 0 on error. - PA_ALWAYS_INLINE uintptr_t AllocNewSuperPage(PartitionRoot<thread_safe>* root, - unsigned int flags) - PA_EXCLUSIVE_LOCKS_REQUIRED(root->lock_); - - // Each bucket allocates a slot span when it runs out of slots. - // A slot span's size is equal to get_pages_per_slot_span() number of - // partition pages. This function initializes all PartitionPage within the - // span to point to the first PartitionPage which holds all the metadata - // for the span (in PartitionPage::SlotSpanMetadata) and registers this bucket - // as the owner of the span. It does NOT put the slots into the bucket's - // freelist. - PA_ALWAYS_INLINE void InitializeSlotSpan( - SlotSpanMetadata<thread_safe>* slot_span); - - // Initializes a super page. Returns the address of the super page's payload. - PA_ALWAYS_INLINE uintptr_t InitializeSuperPage( - PartitionRoot<thread_safe>* root, - uintptr_t super_page, - uintptr_t requested_address) PA_EXCLUSIVE_LOCKS_REQUIRED(root->lock_); - - // Commit 1 or more pages in |slot_span|, enough to get the next slot, which - // is returned by this function. If more slots fit into the committed pages, - // they'll be added to the free list of the slot span (note that next pointers - // are stored inside the slots). - // The free list must be empty when calling this function. - // - // If |slot_span| was freshly allocated, it must have been passed through - // InitializeSlotSpan() first. - PA_ALWAYS_INLINE uintptr_t - ProvisionMoreSlotsAndAllocOne(PartitionRoot<thread_safe>* root, - SlotSpanMetadata<thread_safe>* slot_span) - PA_EXCLUSIVE_LOCKS_REQUIRED(root->lock_); -}; - -} // namespace partition_alloc::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_BUCKET_H_
diff --git a/base/allocator/partition_allocator/partition_bucket_lookup.h b/base/allocator/partition_allocator/partition_bucket_lookup.h deleted file mode 100644 index 127ae31..0000000 --- a/base/allocator/partition_allocator/partition_bucket_lookup.h +++ /dev/null
@@ -1,303 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_BUCKET_LOOKUP_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_BUCKET_LOOKUP_H_ - -#include <cstdint> - -#include "base/allocator/partition_allocator/partition_alloc_base/bits.h" -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" - -namespace partition_alloc::internal { - -// Don't use an anonymous namespace for the constants because it can inhibit -// collapsing them together, even when they are tagged as inline. - -// Precalculate some shift and mask constants used in the hot path. -// Example: malloc(41) == 101001 binary. -// Order is 6 (1 << 6-1) == 32 is highest bit set. -// order_index is the next three MSB == 010 == 2. -// sub_order_index_mask is a mask for the remaining bits == 11 (masking to 01 -// for the sub_order_index). -constexpr uint8_t OrderIndexShift(uint8_t order) { - if (order < kNumBucketsPerOrderBits + 1) { - return 0; - } - - return order - (kNumBucketsPerOrderBits + 1); -} - -constexpr size_t OrderSubIndexMask(uint8_t order) { - if (order == kBitsPerSizeT) { - return static_cast<size_t>(-1) >> (kNumBucketsPerOrderBits + 1); - } - - return ((static_cast<size_t>(1) << order) - 1) >> - (kNumBucketsPerOrderBits + 1); -} - -#if BUILDFLAG(HAS_64_BIT_POINTERS) -#define PA_BITS_PER_SIZE_T 64 -static_assert(kBitsPerSizeT == 64, ""); -#else -#define PA_BITS_PER_SIZE_T 32 -static_assert(kBitsPerSizeT == 32, ""); -#endif // BUILDFLAG(HAS_64_BIT_POINTERS) - -inline constexpr uint8_t kOrderIndexShift[PA_BITS_PER_SIZE_T + 1] = { - OrderIndexShift(0), OrderIndexShift(1), OrderIndexShift(2), - OrderIndexShift(3), OrderIndexShift(4), OrderIndexShift(5), - OrderIndexShift(6), OrderIndexShift(7), OrderIndexShift(8), - OrderIndexShift(9), OrderIndexShift(10), OrderIndexShift(11), - OrderIndexShift(12), OrderIndexShift(13), OrderIndexShift(14), - OrderIndexShift(15), OrderIndexShift(16), OrderIndexShift(17), - OrderIndexShift(18), OrderIndexShift(19), OrderIndexShift(20), - OrderIndexShift(21), OrderIndexShift(22), OrderIndexShift(23), - OrderIndexShift(24), OrderIndexShift(25), OrderIndexShift(26), - OrderIndexShift(27), OrderIndexShift(28), OrderIndexShift(29), - OrderIndexShift(30), OrderIndexShift(31), OrderIndexShift(32), -#if PA_BITS_PER_SIZE_T == 64 - OrderIndexShift(33), OrderIndexShift(34), OrderIndexShift(35), - OrderIndexShift(36), OrderIndexShift(37), OrderIndexShift(38), - OrderIndexShift(39), OrderIndexShift(40), OrderIndexShift(41), - OrderIndexShift(42), OrderIndexShift(43), OrderIndexShift(44), - OrderIndexShift(45), OrderIndexShift(46), OrderIndexShift(47), - OrderIndexShift(48), OrderIndexShift(49), OrderIndexShift(50), - OrderIndexShift(51), OrderIndexShift(52), OrderIndexShift(53), - OrderIndexShift(54), OrderIndexShift(55), OrderIndexShift(56), - OrderIndexShift(57), OrderIndexShift(58), OrderIndexShift(59), - OrderIndexShift(60), OrderIndexShift(61), OrderIndexShift(62), - OrderIndexShift(63), OrderIndexShift(64) -#endif -}; - -inline constexpr size_t kOrderSubIndexMask[PA_BITS_PER_SIZE_T + 1] = { - OrderSubIndexMask(0), OrderSubIndexMask(1), OrderSubIndexMask(2), - OrderSubIndexMask(3), OrderSubIndexMask(4), OrderSubIndexMask(5), - OrderSubIndexMask(6), OrderSubIndexMask(7), OrderSubIndexMask(8), - OrderSubIndexMask(9), OrderSubIndexMask(10), OrderSubIndexMask(11), - OrderSubIndexMask(12), OrderSubIndexMask(13), OrderSubIndexMask(14), - OrderSubIndexMask(15), OrderSubIndexMask(16), OrderSubIndexMask(17), - OrderSubIndexMask(18), OrderSubIndexMask(19), OrderSubIndexMask(20), - OrderSubIndexMask(21), OrderSubIndexMask(22), OrderSubIndexMask(23), - OrderSubIndexMask(24), OrderSubIndexMask(25), OrderSubIndexMask(26), - OrderSubIndexMask(27), OrderSubIndexMask(28), OrderSubIndexMask(29), - OrderSubIndexMask(30), OrderSubIndexMask(31), OrderSubIndexMask(32), -#if PA_BITS_PER_SIZE_T == 64 - OrderSubIndexMask(33), OrderSubIndexMask(34), OrderSubIndexMask(35), - OrderSubIndexMask(36), OrderSubIndexMask(37), OrderSubIndexMask(38), - OrderSubIndexMask(39), OrderSubIndexMask(40), OrderSubIndexMask(41), - OrderSubIndexMask(42), OrderSubIndexMask(43), OrderSubIndexMask(44), - OrderSubIndexMask(45), OrderSubIndexMask(46), OrderSubIndexMask(47), - OrderSubIndexMask(48), OrderSubIndexMask(49), OrderSubIndexMask(50), - OrderSubIndexMask(51), OrderSubIndexMask(52), OrderSubIndexMask(53), - OrderSubIndexMask(54), OrderSubIndexMask(55), OrderSubIndexMask(56), - OrderSubIndexMask(57), OrderSubIndexMask(58), OrderSubIndexMask(59), - OrderSubIndexMask(60), OrderSubIndexMask(61), OrderSubIndexMask(62), - OrderSubIndexMask(63), OrderSubIndexMask(64) -#endif -}; - -// The class used to generate the bucket lookup table at compile-time. -class BucketIndexLookup final { - public: - PA_ALWAYS_INLINE static constexpr uint16_t GetIndexForDefaultBuckets( - size_t size); - PA_ALWAYS_INLINE static constexpr uint16_t GetIndexForDenserBuckets( - size_t size); - PA_ALWAYS_INLINE static constexpr uint16_t GetIndex(size_t size); - - constexpr BucketIndexLookup() { - constexpr uint16_t sentinel_bucket_index = kNumBuckets; - - InitBucketSizes(); - - uint16_t* bucket_index_ptr = &bucket_index_lookup_[0]; - uint16_t bucket_index = 0; - - // Very small allocations, smaller than the first bucketed order -> - // everything goes to the first bucket. - for (uint8_t order = 0; order < kMinBucketedOrder; ++order) { - for (uint16_t j = 0; j < kNumBucketsPerOrder; ++j) { - *bucket_index_ptr++ = 0; - } - } - - // Normal buckets. - for (uint8_t order = kMinBucketedOrder; order <= kMaxBucketedOrder; - ++order) { - size_t size = static_cast<size_t>(1) << (order - 1); - size_t current_increment = size >> kNumBucketsPerOrderBits; - for (uint16_t j = 0; j < kNumBucketsPerOrder; ++j) { - *bucket_index_ptr++ = bucket_index; - - // For small sizes, buckets are close together (current_increment is - // small). For instance, for: - // - kAlignment == 16 (which is the case on most 64 bit systems) - // - kNumBucketsPerOrder == 4 - // - // The 3 next buckets after 16 are {20, 24, 28}. None of these are a - // multiple of kAlignment, so they use the next bucket, that is 32 here. - if (size % kAlignment != 0) { - PA_DCHECK(bucket_sizes_[bucket_index] > size); - // Do not increment bucket_index, since in the example above - // current_size may be 20, and bucket_sizes_[bucket_index] == 32. - } else { - PA_DCHECK(bucket_sizes_[bucket_index] == size); - bucket_index++; - } - - size += current_increment; - } - } - - // Direct-mapped, and overflow. - for (uint8_t order = kMaxBucketedOrder + 1; order <= kBitsPerSizeT; - ++order) { - for (uint16_t j = 0; j < kNumBucketsPerOrder; ++j) { - *bucket_index_ptr++ = sentinel_bucket_index; - } - } - - // Smaller because some buckets are not valid due to alignment constraints. - PA_DCHECK(bucket_index < kNumBuckets); - PA_DCHECK(bucket_index_ptr == bucket_index_lookup_ + ((kBitsPerSizeT + 1) * - kNumBucketsPerOrder)); - // And there's one last bucket lookup that will be hit for e.g. malloc(-1), - // which tries to overflow to a non-existent order. - *bucket_index_ptr = sentinel_bucket_index; - } - constexpr const size_t* bucket_sizes() const { return &bucket_sizes_[0]; } - - private: - constexpr void InitBucketSizes() { - size_t current_size = kSmallestBucket; - size_t current_increment = kSmallestBucket >> kNumBucketsPerOrderBits; - size_t* bucket_size = &bucket_sizes_[0]; - for (size_t i = 0; i < kNumBucketedOrders; ++i) { - for (size_t j = 0; j < kNumBucketsPerOrder; ++j) { - // All bucket sizes have to be multiples of kAlignment, skip otherwise. - if (current_size % kAlignment == 0) { - *bucket_size = current_size; - ++bucket_size; - } - current_size += current_increment; - } - current_increment <<= 1; - } - - // The remaining buckets are invalid. - while (bucket_size < bucket_sizes_ + kNumBuckets) { - *(bucket_size++) = kInvalidBucketSize; - } - } - - size_t bucket_sizes_[kNumBuckets]{}; - // The bucket lookup table lets us map a size_t to a bucket quickly. - // The trailing +1 caters for the overflow case for very large allocation - // sizes. It is one flat array instead of a 2D array because in the 2D - // world, we'd need to index array[blah][max+1] which risks undefined - // behavior. - uint16_t - bucket_index_lookup_[((kBitsPerSizeT + 1) * kNumBucketsPerOrder) + 1]{}; -}; - -PA_ALWAYS_INLINE constexpr size_t RoundUpToPowerOfTwo(size_t size) { - const size_t n = 1 << base::bits::Log2Ceiling(static_cast<uint32_t>(size)); - PA_DCHECK(size <= n); - return n; -} - -PA_ALWAYS_INLINE constexpr size_t RoundUpSize(size_t size) { - const size_t next_power = RoundUpToPowerOfTwo(size); - const size_t prev_power = next_power >> 1; - PA_DCHECK(size <= next_power); - PA_DCHECK(prev_power < size); - if (size <= prev_power * 5 / 4) { - return prev_power * 5 / 4; - } else { - return next_power; - } -} - -PA_ALWAYS_INLINE constexpr uint16_t RoundUpToOdd(uint16_t size) { - return (size % 2 == 0) + size; -} - -// static -PA_ALWAYS_INLINE constexpr uint16_t BucketIndexLookup::GetIndexForDenserBuckets( - size_t size) { - // This forces the bucket table to be constant-initialized and immediately - // materialized in the binary. - constexpr BucketIndexLookup lookup{}; - const size_t order = - kBitsPerSizeT - - static_cast<size_t>(base::bits::CountLeadingZeroBits(size)); - // The order index is simply the next few bits after the most significant - // bit. - const size_t order_index = - (size >> kOrderIndexShift[order]) & (kNumBucketsPerOrder - 1); - // And if the remaining bits are non-zero we must bump the bucket up. - const size_t sub_order_index = size & kOrderSubIndexMask[order]; - const uint16_t index = - lookup.bucket_index_lookup_[(order << kNumBucketsPerOrderBits) + - order_index + !!sub_order_index]; - PA_DCHECK(index <= kNumBuckets); // Last one is the sentinel bucket. - return index; -} - -// static -PA_ALWAYS_INLINE constexpr uint16_t -BucketIndexLookup::GetIndexForDefaultBuckets(size_t size) { - const auto index = GetIndexForDenserBuckets(size); - // Below the minimum size, 4 and 8 bucket distributions are the same, since we - // can't fit any more buckets per order; this is due to alignment - // requirements: each bucket must be a multiple of the alignment, which - // implies the difference between buckets must also be a multiple of the - // alignment. In smaller orders, this limits the number of buckets we can - // have per order. So, for these small order, we do not want to skip every - // second bucket. - // - // We also do not want to go about the index for the max bucketed size. - if (size > kAlignment * kNumBucketsPerOrder && - index < GetIndexForDenserBuckets(kMaxBucketed)) { - return RoundUpToOdd(index); - } else { - return index; - } -} - -// static -PA_ALWAYS_INLINE constexpr uint16_t BucketIndexLookup::GetIndex(size_t size) { - // For any order 2^N, under the denser bucket distribution ("Distribution A"), - // we have 4 evenly distributed buckets: 2^N, 1.25*2^N, 1.5*2^N, and 1.75*2^N. - // These numbers represent the maximum size of an allocation that can go into - // a given bucket. - // - // Under the less dense bucket distribution ("Distribution B"), we only have - // 2 buckets for the same order 2^N: 2^N and 1.25*2^N. - // - // Everything that would be mapped to the last two buckets of an order under - // Distribution A is instead mapped to the first bucket of the next order - // under Distribution B. The following diagram shows roughly what this looks - // like for the order starting from 2^10, as an example. - // - // A: ... | 2^10 | 1.25*2^10 | 1.5*2^10 | 1.75*2^10 | 2^11 | ... - // B: ... | 2^10 | 1.25*2^10 | -------- | --------- | 2^11 | ... - // - // So, an allocation of size 1.4*2^10 would go into the 1.5*2^10 bucket under - // Distribution A, but to the 2^11 bucket under Distribution B. - if (1 << 8 < size && size < kHighThresholdForAlternateDistribution) { - return BucketIndexLookup::GetIndexForDefaultBuckets(RoundUpSize(size)); - } - return BucketIndexLookup::GetIndexForDefaultBuckets(size); -} - -} // namespace partition_alloc::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_BUCKET_LOOKUP_H_
diff --git a/base/allocator/partition_allocator/partition_cookie.h b/base/allocator/partition_allocator/partition_cookie.h deleted file mode 100644 index 7c6b4a2..0000000 --- a/base/allocator/partition_allocator/partition_cookie.h +++ /dev/null
@@ -1,49 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_COOKIE_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_COOKIE_H_ - -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" - -namespace partition_alloc::internal { - -static constexpr size_t kCookieSize = 16; - -// Cookie is enabled for debug builds. -#if BUILDFLAG(PA_DCHECK_IS_ON) - -inline constexpr unsigned char kCookieValue[kCookieSize] = { - 0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xD0, 0x0D, - 0x13, 0x37, 0xF0, 0x05, 0xBA, 0x11, 0xAB, 0x1E}; - -constexpr size_t kPartitionCookieSizeAdjustment = kCookieSize; - -PA_ALWAYS_INLINE void PartitionCookieCheckValue(unsigned char* cookie_ptr) { - for (size_t i = 0; i < kCookieSize; ++i, ++cookie_ptr) { - PA_DCHECK(*cookie_ptr == kCookieValue[i]); - } -} - -PA_ALWAYS_INLINE void PartitionCookieWriteValue(unsigned char* cookie_ptr) { - for (size_t i = 0; i < kCookieSize; ++i, ++cookie_ptr) { - *cookie_ptr = kCookieValue[i]; - } -} - -#else - -constexpr size_t kPartitionCookieSizeAdjustment = 0; - -PA_ALWAYS_INLINE void PartitionCookieCheckValue(unsigned char* address) {} - -PA_ALWAYS_INLINE void PartitionCookieWriteValue(unsigned char* cookie_ptr) {} - -#endif // BUILDFLAG(PA_DCHECK_IS_ON) - -} // namespace partition_alloc::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_COOKIE_H_
diff --git a/base/allocator/partition_allocator/partition_direct_map_extent.h b/base/allocator/partition_allocator/partition_direct_map_extent.h deleted file mode 100644 index 4d9bb61..0000000 --- a/base/allocator/partition_allocator/partition_direct_map_extent.h +++ /dev/null
@@ -1,75 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_DIRECT_MAP_EXTENT_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_DIRECT_MAP_EXTENT_H_ - -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/partition_bucket.h" -#include "base/allocator/partition_allocator/partition_page.h" - -namespace partition_alloc::internal { - -template <bool thread_safe> -struct PartitionDirectMapExtent { - PartitionDirectMapExtent<thread_safe>* next_extent; - PartitionDirectMapExtent<thread_safe>* prev_extent; - PartitionBucket<thread_safe>* bucket; - // Size of the entire reservation, including guard pages, meta-data, - // padding for alignment before allocation, and padding for granularity at the - // end of the allocation. - size_t reservation_size; - // Padding between the first partition page (guard pages + meta-data) and - // the allocation. - size_t padding_for_alignment; - - PA_ALWAYS_INLINE static PartitionDirectMapExtent<thread_safe>* FromSlotSpan( - SlotSpanMetadata<thread_safe>* slot_span); -}; - -// Metadata page for direct-mapped allocations. -template <bool thread_safe> -struct PartitionDirectMapMetadata { - // |page| and |subsequent_page| are needed to match the layout of normal - // buckets (specifically, of single-slot slot spans), with the caveat that - // only the first subsequent page is needed (for SubsequentPageMetadata) and - // others aren't used for direct map. - PartitionPage<thread_safe> page; - PartitionPage<thread_safe> subsequent_page; - // The following fields are metadata specific to direct map allocations. All - // these fields will easily fit into the precalculated metadata region, - // because a direct map allocation starts no further than half way through the - // super page. - PartitionBucket<thread_safe> bucket; - PartitionDirectMapExtent<thread_safe> direct_map_extent; - - PA_ALWAYS_INLINE static PartitionDirectMapMetadata<thread_safe>* FromSlotSpan( - SlotSpanMetadata<thread_safe>* slot_span); -}; - -template <bool thread_safe> -PA_ALWAYS_INLINE PartitionDirectMapMetadata<thread_safe>* -PartitionDirectMapMetadata<thread_safe>::FromSlotSpan( - SlotSpanMetadata<thread_safe>* slot_span) { - PA_DCHECK(slot_span->bucket->is_direct_mapped()); - // |*slot_span| is the first field of |PartitionDirectMapMetadata|, just cast. - auto* metadata = - reinterpret_cast<PartitionDirectMapMetadata<thread_safe>*>(slot_span); - PA_DCHECK(&metadata->page.slot_span_metadata == slot_span); - return metadata; -} - -template <bool thread_safe> -PA_ALWAYS_INLINE PartitionDirectMapExtent<thread_safe>* -PartitionDirectMapExtent<thread_safe>::FromSlotSpan( - SlotSpanMetadata<thread_safe>* slot_span) { - PA_DCHECK(slot_span->bucket->is_direct_mapped()); - return &PartitionDirectMapMetadata<thread_safe>::FromSlotSpan(slot_span) - ->direct_map_extent; -} - -} // namespace partition_alloc::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_DIRECT_MAP_EXTENT_H_
diff --git a/base/allocator/partition_allocator/partition_freelist_entry.h b/base/allocator/partition_allocator/partition_freelist_entry.h deleted file mode 100644 index e319339..0000000 --- a/base/allocator/partition_allocator/partition_freelist_entry.h +++ /dev/null
@@ -1,347 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_FREELIST_ENTRY_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_FREELIST_ENTRY_H_ - -#include <cstddef> -#include <cstdint> - -#include "base/allocator/partition_allocator/freeslot_bitmap.h" -#include "base/allocator/partition_allocator/partition_alloc-inl.h" -#include "base/allocator/partition_allocator/partition_alloc_base/bits.h" -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/partition_alloc_config.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "base/allocator/partition_allocator/partition_ref_count.h" -#include "build/build_config.h" - -#if !defined(ARCH_CPU_BIG_ENDIAN) -#include "base/allocator/partition_allocator/reverse_bytes.h" -#endif // !defined(ARCH_CPU_BIG_ENDIAN) - -namespace partition_alloc::internal { - -namespace { - -[[noreturn]] PA_NOINLINE void FreelistCorruptionDetected(size_t extra) { - // Make it visible in minidumps. - PA_DEBUG_DATA_ON_STACK("extra", extra); - PA_IMMEDIATE_CRASH(); -} - -} // namespace - -class PartitionFreelistEntry; - -class EncodedPartitionFreelistEntryPtr { - private: - PA_ALWAYS_INLINE constexpr explicit EncodedPartitionFreelistEntryPtr( - std::nullptr_t) - : encoded_(Transform(0)) {} - PA_ALWAYS_INLINE explicit EncodedPartitionFreelistEntryPtr(void* ptr) - // The encoded pointer stays MTE-tagged. - : encoded_(Transform(reinterpret_cast<uintptr_t>(ptr))) {} - - PA_ALWAYS_INLINE PartitionFreelistEntry* Decode() const { - return reinterpret_cast<PartitionFreelistEntry*>(Transform(encoded_)); - } - - PA_ALWAYS_INLINE constexpr uintptr_t Inverted() const { return ~encoded_; } - - PA_ALWAYS_INLINE constexpr void Override(uintptr_t encoded) { - encoded_ = encoded; - } - - PA_ALWAYS_INLINE constexpr explicit operator bool() const { return encoded_; } - - // Transform() works the same in both directions, so can be used for - // encoding and decoding. - PA_ALWAYS_INLINE static constexpr uintptr_t Transform(uintptr_t address) { - // We use bswap on little endian as a fast transformation for two reasons: - // 1) On 64 bit architectures, the pointer is very unlikely to be a - // canonical address. Therefore, if an object is freed and its vtable is - // used where the attacker doesn't get the chance to run allocations - // between the free and use, the vtable dereference is likely to fault. - // 2) If the attacker has a linear buffer overflow and elects to try and - // corrupt a freelist pointer, partial pointer overwrite attacks are - // thwarted. - // For big endian, similar guarantees are arrived at with a negation. -#if defined(ARCH_CPU_BIG_ENDIAN) - uintptr_t transformed = ~address; -#else - uintptr_t transformed = ReverseBytes(address); -#endif - return transformed; - } - - uintptr_t encoded_; - - friend PartitionFreelistEntry; -}; - -// Freelist entries are encoded for security reasons. See -// //base/allocator/partition_allocator/PartitionAlloc.md and |Transform()| for -// the rationale and mechanism, respectively. -class PartitionFreelistEntry { - private: - constexpr explicit PartitionFreelistEntry(std::nullptr_t) - : encoded_next_(EncodedPartitionFreelistEntryPtr(nullptr)) -#if PA_CONFIG(HAS_FREELIST_SHADOW_ENTRY) - , - shadow_(encoded_next_.Inverted()) -#endif - { - } - explicit PartitionFreelistEntry(PartitionFreelistEntry* next) - : encoded_next_(EncodedPartitionFreelistEntryPtr(next)) -#if PA_CONFIG(HAS_FREELIST_SHADOW_ENTRY) - , - shadow_(encoded_next_.Inverted()) -#endif - { - } - // For testing only. - PartitionFreelistEntry(void* next, bool make_shadow_match) - : encoded_next_(EncodedPartitionFreelistEntryPtr(next)) -#if PA_CONFIG(HAS_FREELIST_SHADOW_ENTRY) - , - shadow_(make_shadow_match ? encoded_next_.Inverted() : 12345) -#endif - { - } - - public: - ~PartitionFreelistEntry() = delete; - - // Emplaces the freelist entry at the beginning of the given slot span, and - // initializes it as null-terminated. - PA_ALWAYS_INLINE static PartitionFreelistEntry* EmplaceAndInitNull( - void* slot_start_tagged) { - // |slot_start_tagged| is MTE-tagged. - auto* entry = new (slot_start_tagged) PartitionFreelistEntry(nullptr); - return entry; - } - PA_ALWAYS_INLINE static PartitionFreelistEntry* EmplaceAndInitNull( - uintptr_t slot_start) { - return EmplaceAndInitNull(SlotStartAddr2Ptr(slot_start)); - } - - // Emplaces the freelist entry at the beginning of the given slot span, and - // initializes it with the given |next| pointer, but encoded. - // - // This freelist is built for the purpose of thread-cache. This means that we - // can't perform a check that this and the next pointer belong to the same - // super page, as thread-cache spans may chain slots across super pages. - PA_ALWAYS_INLINE static PartitionFreelistEntry* EmplaceAndInitForThreadCache( - uintptr_t slot_start, - PartitionFreelistEntry* next) { - auto* entry = - new (SlotStartAddr2Ptr(slot_start)) PartitionFreelistEntry(next); - return entry; - } - - // Emplaces the freelist entry at the beginning of the given slot span, and - // initializes it with the given |next| pointer. - // - // This is for testing purposes only! |make_shadow_match| allows you to choose - // if the shadow matches the next pointer properly or is trash. - PA_ALWAYS_INLINE static void EmplaceAndInitForTest(uintptr_t slot_start, - void* next, - bool make_shadow_match) { - new (SlotStartAddr2Ptr(slot_start)) - PartitionFreelistEntry(next, make_shadow_match); - } - - void CorruptNextForTesting(uintptr_t v) { - // We just need a value that can never be a valid pointer here. - encoded_next_.Override(EncodedPartitionFreelistEntryPtr::Transform(v)); - } - - // Puts |extra| on the stack before crashing in case of memory - // corruption. Meant to be used to report the failed allocation size. - template <bool crash_on_corruption> - PA_ALWAYS_INLINE PartitionFreelistEntry* GetNextForThreadCache( - size_t extra) const; - PA_ALWAYS_INLINE PartitionFreelistEntry* GetNext(size_t extra) const; - - PA_NOINLINE void CheckFreeList(size_t extra) const { - for (auto* entry = this; entry; entry = entry->GetNext(extra)) { - // |GetNext()| checks freelist integrity. - } - } - - PA_NOINLINE void CheckFreeListForThreadCache(size_t extra) const { - for (auto* entry = this; entry; - entry = entry->GetNextForThreadCache<true>(extra)) { - // |GetNextForThreadCache()| checks freelist integrity. - } - } - - PA_ALWAYS_INLINE void SetNext(PartitionFreelistEntry* entry) { - // SetNext() is either called on the freelist head, when provisioning new - // slots, or when GetNext() has been called before, no need to pass the - // size. -#if BUILDFLAG(PA_DCHECK_IS_ON) - // Regular freelists always point to an entry within the same super page. - // - // This is most likely a PartitionAlloc bug if this triggers. - if (PA_UNLIKELY(entry && - (SlotStartPtr2Addr(this) & kSuperPageBaseMask) != - (SlotStartPtr2Addr(entry) & kSuperPageBaseMask))) { - FreelistCorruptionDetected(0); - } -#endif // BUILDFLAG(PA_DCHECK_IS_ON) - - encoded_next_ = EncodedPartitionFreelistEntryPtr(entry); -#if PA_CONFIG(HAS_FREELIST_SHADOW_ENTRY) - shadow_ = encoded_next_.Inverted(); -#endif - } - - // Zeroes out |this| before returning the slot. The pointer to this memory - // will be returned to the user (caller of Alloc()), thus can't have internal - // data. - PA_ALWAYS_INLINE uintptr_t ClearForAllocation() { - encoded_next_.Override(0); -#if PA_CONFIG(HAS_FREELIST_SHADOW_ENTRY) - shadow_ = 0; -#endif - return SlotStartPtr2Addr(this); - } - - PA_ALWAYS_INLINE constexpr bool IsEncodedNextPtrZero() const { - return !encoded_next_; - } - - private: - template <bool crash_on_corruption> - PA_ALWAYS_INLINE PartitionFreelistEntry* GetNextInternal( - size_t extra, - bool for_thread_cache) const; - - PA_ALWAYS_INLINE static bool IsSane(const PartitionFreelistEntry* here, - const PartitionFreelistEntry* next, - bool for_thread_cache) { - // Don't allow the freelist to be blindly followed to any location. - // Checks two constraints: - // - here and next must belong to the same superpage, unless this is in the - // thread cache (they even always belong to the same slot span). - // - next cannot point inside the metadata area. - // - // Also, the lightweight UaF detection (pointer shadow) is checked. - - uintptr_t here_address = SlotStartPtr2Addr(here); - uintptr_t next_address = SlotStartPtr2Addr(next); - -#if PA_CONFIG(HAS_FREELIST_SHADOW_ENTRY) - bool shadow_ptr_ok = here->encoded_next_.Inverted() == here->shadow_; -#else - bool shadow_ptr_ok = true; -#endif - - bool same_superpage = (here_address & kSuperPageBaseMask) == - (next_address & kSuperPageBaseMask); -#if BUILDFLAG(USE_FREESLOT_BITMAP) - bool marked_as_free_in_bitmap = - for_thread_cache ? true : !FreeSlotBitmapSlotIsUsed(next_address); -#else - bool marked_as_free_in_bitmap = true; -#endif - - // This is necessary but not sufficient when quarantine is enabled, see - // SuperPagePayloadBegin() in partition_page.h. However we don't want to - // fetch anything from the root in this function. - bool not_in_metadata = - (next_address & kSuperPageOffsetMask) >= PartitionPageSize(); - - if (for_thread_cache) { - return shadow_ptr_ok & not_in_metadata; - } else { - return shadow_ptr_ok & same_superpage & marked_as_free_in_bitmap & - not_in_metadata; - } - } - - EncodedPartitionFreelistEntryPtr encoded_next_; - // This is intended to detect unintentional corruptions of the freelist. - // These can happen due to a Use-after-Free, or overflow of the previous - // allocation in the slot span. -#if PA_CONFIG(HAS_FREELIST_SHADOW_ENTRY) - uintptr_t shadow_; -#endif -}; - -static_assert(kSmallestBucket >= sizeof(PartitionFreelistEntry), - "Need enough space for freelist entries in the smallest slot"); -#if BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) -// The smallest bucket actually used. Note that the smallest request is 1 (if -// it's 0, it gets patched to 1), and ref-count gets added to it. -namespace { -constexpr size_t kSmallestUsedBucket = - base::bits::AlignUp(1 + sizeof(PartitionRefCount), kSmallestBucket); -} -static_assert(kSmallestUsedBucket >= - sizeof(PartitionFreelistEntry) + sizeof(PartitionRefCount), - "Need enough space for freelist entries and the ref-count in the " - "smallest *used* slot"); -#endif // BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) - -template <bool crash_on_corruption> -PA_ALWAYS_INLINE PartitionFreelistEntry* -PartitionFreelistEntry::GetNextInternal(size_t extra, - bool for_thread_cache) const { - // GetNext() can be called on discarded memory, in which case |encoded_next_| - // is 0, and none of the checks apply. Don't prefetch nullptr either. - if (IsEncodedNextPtrZero()) { - return nullptr; - } - - auto* ret = encoded_next_.Decode(); - // We rely on constant propagation to remove the branches coming from - // |for_thread_cache|, since the argument is always a compile-time constant. - if (PA_UNLIKELY(!IsSane(this, ret, for_thread_cache))) { - if constexpr (crash_on_corruption) { - // Put the corrupted data on the stack, it may give us more information - // about what kind of corruption that was. - PA_DEBUG_DATA_ON_STACK("first", - static_cast<size_t>(encoded_next_.encoded_)); -#if PA_CONFIG(HAS_FREELIST_SHADOW_ENTRY) - PA_DEBUG_DATA_ON_STACK("second", static_cast<size_t>(shadow_)); -#endif - FreelistCorruptionDetected(extra); - } else { - return nullptr; - } - } - - // In real-world profiles, the load of |encoded_next_| above is responsible - // for a large fraction of the allocation cost. However, we cannot anticipate - // it enough since it is accessed right after we know its address. - // - // In the case of repeated allocations, we can prefetch the access that will - // be done at the *next* allocation, which will touch *ret, prefetch it. - PA_PREFETCH(ret); - - return ret; -} - -template <bool crash_on_corruption> -PA_ALWAYS_INLINE PartitionFreelistEntry* -PartitionFreelistEntry::GetNextForThreadCache(size_t extra) const { - return GetNextInternal<crash_on_corruption>(extra, true); -} - -PA_ALWAYS_INLINE PartitionFreelistEntry* PartitionFreelistEntry::GetNext( - size_t extra) const { - return GetNextInternal<true>(extra, false); -} - -} // namespace partition_alloc::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_FREELIST_ENTRY_H_
diff --git a/base/allocator/partition_allocator/partition_lock.h b/base/allocator/partition_allocator/partition_lock.h deleted file mode 100644 index 47ccb42..0000000 --- a/base/allocator/partition_allocator/partition_lock.h +++ /dev/null
@@ -1,146 +0,0 @@ -// 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 BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_LOCK_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_LOCK_H_ - -#include <atomic> -#include <type_traits> - -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h" -#include "base/allocator/partition_allocator/partition_alloc_base/thread_annotations.h" -#include "base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/pkey.h" -#include "base/allocator/partition_allocator/spinning_mutex.h" -#include "build/build_config.h" - -namespace partition_alloc::internal { - -class PA_LOCKABLE Lock { - public: - inline constexpr Lock(); - void Acquire() PA_EXCLUSIVE_LOCK_FUNCTION() { -#if BUILDFLAG(PA_DCHECK_IS_ON) -#if BUILDFLAG(ENABLE_PKEYS) - LiftPkeyRestrictionsScope lift_pkey_restrictions; -#endif - - // When PartitionAlloc is malloc(), it can easily become reentrant. For - // instance, a DCHECK() triggers in external code (such as - // base::Lock). DCHECK() error message formatting allocates, which triggers - // PartitionAlloc, and then we get reentrancy, and in this case infinite - // recursion. - // - // To avoid that, crash quickly when the code becomes reentrant. - base::PlatformThreadRef current_thread = base::PlatformThread::CurrentRef(); - if (!lock_.Try()) { - // The lock wasn't free when we tried to acquire it. This can be because - // another thread or *this* thread was holding it. - // - // If it's this thread holding it, then it cannot have become free in the - // meantime, and the current value of |owning_thread_ref_| is valid, as it - // was set by this thread. Assuming that writes to |owning_thread_ref_| - // are atomic, then if it's us, we are trying to recursively acquire a - // non-recursive lock. - // - // Note that we don't rely on a DCHECK() in base::Lock(), as it would - // itself allocate. Meaning that without this code, a reentrancy issue - // hangs on Linux. - if (PA_UNLIKELY(owning_thread_ref_.load(std::memory_order_acquire) == - current_thread)) { - // Trying to acquire lock while it's held by this thread: reentrancy - // issue. - PA_IMMEDIATE_CRASH(); - } - lock_.Acquire(); - } - owning_thread_ref_.store(current_thread, std::memory_order_release); -#else - lock_.Acquire(); -#endif - } - - void Release() PA_UNLOCK_FUNCTION() { -#if BUILDFLAG(PA_DCHECK_IS_ON) - owning_thread_ref_.store(base::PlatformThreadRef(), - std::memory_order_release); -#endif - lock_.Release(); - } - void AssertAcquired() const PA_ASSERT_EXCLUSIVE_LOCK() { - lock_.AssertAcquired(); -#if BUILDFLAG(PA_DCHECK_IS_ON) -#if BUILDFLAG(ENABLE_PKEYS) - LiftPkeyRestrictionsScope lift_pkey_restrictions; -#endif - PA_DCHECK(owning_thread_ref_.load(std ::memory_order_acquire) == - base::PlatformThread::CurrentRef()); -#endif - } - - void Reinit() PA_UNLOCK_FUNCTION() { - lock_.AssertAcquired(); -#if BUILDFLAG(PA_DCHECK_IS_ON) - owning_thread_ref_.store(base::PlatformThreadRef(), - std::memory_order_release); -#endif - lock_.Reinit(); - } - - private: - SpinningMutex lock_; - -#if BUILDFLAG(PA_DCHECK_IS_ON) - // Should in theory be protected by |lock_|, but we need to read it to detect - // recursive lock acquisition (and thus, the allocator becoming reentrant). - std::atomic<base::PlatformThreadRef> owning_thread_ref_ = - base::PlatformThreadRef(); -#endif -}; - -class PA_SCOPED_LOCKABLE ScopedGuard { - public: - explicit ScopedGuard(Lock& lock) PA_EXCLUSIVE_LOCK_FUNCTION(lock) - : lock_(lock) { - lock_.Acquire(); - } - ~ScopedGuard() PA_UNLOCK_FUNCTION() { lock_.Release(); } - - private: - Lock& lock_; -}; - -class PA_SCOPED_LOCKABLE ScopedUnlockGuard { - public: - explicit ScopedUnlockGuard(Lock& lock) PA_UNLOCK_FUNCTION(lock) - : lock_(lock) { - lock_.Release(); - } - ~ScopedUnlockGuard() PA_EXCLUSIVE_LOCK_FUNCTION() { lock_.Acquire(); } - - private: - Lock& lock_; -}; - -constexpr Lock::Lock() = default; - -// We want PartitionRoot to not have a global destructor, so this should not -// have one. -static_assert(std::is_trivially_destructible<Lock>::value, ""); - -} // namespace partition_alloc::internal - -namespace base { -namespace internal { - -using PartitionLock = ::partition_alloc::internal::Lock; -using PartitionAutoLock = ::partition_alloc::internal::ScopedGuard; - -} // namespace internal -} // namespace base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_LOCK_H_
diff --git a/base/allocator/partition_allocator/partition_lock_perftest.cc b/base/allocator/partition_allocator/partition_lock_perftest.cc deleted file mode 100644 index ec57f4f..0000000 --- a/base/allocator/partition_allocator/partition_lock_perftest.cc +++ /dev/null
@@ -1,127 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_lock.h" - -#include <vector> - -#include "base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_for_testing.h" -#include "base/allocator/partition_allocator/partition_alloc_base/time/time.h" -#include "base/timer/lap_timer.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/perf/perf_result_reporter.h" - -namespace partition_alloc::internal { - -namespace { - -constexpr int kWarmupRuns = 1; -constexpr ::base::TimeDelta kTimeLimit = ::base::Seconds(1); -constexpr int kTimeCheckInterval = 100000; - -constexpr char kMetricPrefixLock[] = "PartitionLock."; -constexpr char kMetricLockUnlockThroughput[] = "lock_unlock_throughput"; -constexpr char kMetricLockUnlockLatency[] = "lock_unlock_latency_ns"; -constexpr char kStoryBaseline[] = "baseline_story"; -constexpr char kStoryWithCompetingThread[] = "with_competing_thread"; - -perf_test::PerfResultReporter SetUpReporter(const std::string& story_name) { - perf_test::PerfResultReporter reporter(kMetricPrefixLock, story_name); - reporter.RegisterImportantMetric(kMetricLockUnlockThroughput, "runs/s"); - reporter.RegisterImportantMetric(kMetricLockUnlockLatency, "ns"); - return reporter; -} - -class Spin : public base::PlatformThreadForTesting::Delegate { - public: - Spin(Lock* lock, uint32_t* data) - : lock_(lock), data_(data), should_stop_(false) {} - ~Spin() override = default; - - void ThreadMain() override { - started_count_++; - // Local variable to avoid "cache line ping-pong" from influencing the - // results. - uint32_t count = 0; - while (!should_stop_.load(std::memory_order_relaxed)) { - lock_->Acquire(); - count++; - lock_->Release(); - } - - lock_->Acquire(); - (*data_) += count; - lock_->Release(); - } - - // Called from another thread to stop the loop. - void Stop() { should_stop_ = true; } - int started_count() const { return started_count_; } - - private: - Lock* lock_; - uint32_t* data_ GUARDED_BY(lock_); - std::atomic<bool> should_stop_; - std::atomic<int> started_count_{0}; -}; - -} // namespace - -TEST(PartitionLockPerfTest, Simple) { - ::base::LapTimer timer(kWarmupRuns, kTimeLimit, kTimeCheckInterval); - [[maybe_unused]] uint32_t data = 0; - - Lock lock; - - do { - lock.Acquire(); - data += 1; - lock.Release(); - timer.NextLap(); - } while (!timer.HasTimeLimitExpired()); - - auto reporter = SetUpReporter(kStoryBaseline); - reporter.AddResult(kMetricLockUnlockThroughput, timer.LapsPerSecond()); - reporter.AddResult(kMetricLockUnlockLatency, 1e9 / timer.LapsPerSecond()); -} - -TEST(PartitionLockPerfTest, WithCompetingThreads) { - uint32_t data = 0; - - Lock lock; - - // Starts a competing thread executing the same loop as this thread. - Spin thread_main(&lock, &data); - std::vector<base::PlatformThreadHandle> thread_handles; - constexpr int kThreads = 4; - - for (int i = 0; i < kThreads; i++) { - base::PlatformThreadHandle thread_handle; - ASSERT_TRUE(base::PlatformThreadForTesting::Create(0, &thread_main, - &thread_handle)); - thread_handles.push_back(thread_handle); - } - // Wait for all the threads to start. - while (thread_main.started_count() != kThreads) { - } - - ::base::LapTimer timer(kWarmupRuns, kTimeLimit, kTimeCheckInterval); - do { - lock.Acquire(); - data += 1; - lock.Release(); - timer.NextLap(); - } while (!timer.HasTimeLimitExpired()); - - thread_main.Stop(); - for (int i = 0; i < kThreads; i++) { - base::PlatformThreadForTesting::Join(thread_handles[i]); - } - - auto reporter = SetUpReporter(kStoryWithCompetingThread); - reporter.AddResult(kMetricLockUnlockThroughput, timer.LapsPerSecond()); - reporter.AddResult(kMetricLockUnlockLatency, 1e9 / timer.LapsPerSecond()); -} - -} // namespace partition_alloc::internal
diff --git a/base/allocator/partition_allocator/partition_lock_unittest.cc b/base/allocator/partition_allocator/partition_lock_unittest.cc deleted file mode 100644 index 626bc4b..0000000 --- a/base/allocator/partition_allocator/partition_lock_unittest.cc +++ /dev/null
@@ -1,240 +0,0 @@ -// 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. - -#include "base/allocator/partition_allocator/partition_lock.h" - -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_base/thread_annotations.h" -#include "base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread_for_testing.h" -#include "base/allocator/partition_allocator/partition_alloc_base/time/time.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace partition_alloc::internal { - -TEST(PartitionAllocLockTest, Simple) { - Lock lock; - lock.Acquire(); - lock.Release(); -} - -namespace { - -Lock g_lock; - -} // namespace - -TEST(PartitionAllocLockTest, StaticLockStartsUnlocked) { - g_lock.Acquire(); - g_lock.Release(); -} - -namespace { - -class ThreadDelegateForContended - : public base::PlatformThreadForTesting::Delegate { - public: - explicit ThreadDelegateForContended(Lock& start_lock, - Lock& lock, - int iterations, - int& counter) - : start_lock_(start_lock), - lock_(lock), - iterations_(iterations), - counter_(counter) {} - - void ThreadMain() override { - start_lock_.Acquire(); - start_lock_.Release(); - - for (int i = 0; i < iterations_; i++) { - lock_.Acquire(); - ++counter_; - lock_.Release(); - } - } - - private: - Lock& start_lock_; - Lock& lock_; - const int iterations_; - int& counter_; -}; - -} // namespace - -TEST(PartitionAllocLockTest, Contended) { - int counter = 0; // *Not* atomic. - std::vector<internal::base::PlatformThreadHandle> thread_handles; - constexpr int iterations_per_thread = 1000000; - constexpr int num_threads = 4; - - Lock lock; - Lock start_lock; - - ThreadDelegateForContended delegate(start_lock, lock, iterations_per_thread, - counter); - - start_lock.Acquire(); // Make sure that the threads compete, by waiting until - // all of them have at least been created. - for (int i = 0; i < num_threads; ++i) { - base::PlatformThreadHandle handle; - base::PlatformThreadForTesting::Create(0, &delegate, &handle); - thread_handles.push_back(handle); - } - - start_lock.Release(); - - for (int i = 0; i < num_threads; ++i) { - base::PlatformThreadForTesting::Join(thread_handles[i]); - } - EXPECT_EQ(iterations_per_thread * num_threads, counter); -} - -namespace { - -class ThreadDelegateForSlowThreads - : public base::PlatformThreadForTesting::Delegate { - public: - explicit ThreadDelegateForSlowThreads(Lock& start_lock, - Lock& lock, - int iterations, - int& counter) - : start_lock_(start_lock), - lock_(lock), - iterations_(iterations), - counter_(counter) {} - - void ThreadMain() override { - start_lock_.Acquire(); - start_lock_.Release(); - - for (int i = 0; i < iterations_; i++) { - lock_.Acquire(); - ++counter_; - // Hold the lock for a while, to force futex()-based locks to sleep. - base::PlatformThread::Sleep(base::Milliseconds(1)); - lock_.Release(); - } - } - - private: - Lock& start_lock_; - Lock& lock_; - const int iterations_; - int& counter_; -}; - -} // namespace - -TEST(PartitionAllocLockTest, SlowThreads) { - int counter = 0; // *Not* atomic. - std::vector<base::PlatformThreadHandle> thread_handles; - constexpr int iterations_per_thread = 100; - constexpr int num_threads = 4; - - Lock lock; - Lock start_lock; - - ThreadDelegateForSlowThreads delegate(start_lock, lock, iterations_per_thread, - counter); - - start_lock.Acquire(); // Make sure that the threads compete, by waiting until - // all of them have at least been created. - for (int i = 0; i < num_threads; i++) { - base::PlatformThreadHandle handle; - base::PlatformThreadForTesting::Create(0, &delegate, &handle); - thread_handles.push_back(handle); - } - - start_lock.Release(); - - for (int i = 0; i < num_threads; i++) { - base::PlatformThreadForTesting::Join(thread_handles[i]); - } - EXPECT_EQ(iterations_per_thread * num_threads, counter); -} - -TEST(PartitionAllocLockTest, AssertAcquired) { - Lock lock; - lock.Acquire(); - lock.AssertAcquired(); - lock.Release(); -} - -// AssertAcquired() is only enforced with DCHECK()s. -#if defined(GTEST_HAS_DEATH_TEST) && BUILDFLAG(PA_DCHECK_IS_ON) - -TEST(PartitionAllocLockTest, AssertAcquiredDeathTest) { - Lock lock; - EXPECT_DEATH(lock.AssertAcquired(), ""); -} - -namespace { - -class ThreadDelegateForAssertAcquiredAnotherThreadHoldsTheLock - : public base::PlatformThreadForTesting::Delegate { - public: - explicit ThreadDelegateForAssertAcquiredAnotherThreadHoldsTheLock(Lock& lock) - : lock_(lock) {} - - void ThreadMain() PA_NO_THREAD_SAFETY_ANALYSIS override { lock_.Acquire(); } - - private: - Lock& lock_; -}; - -} // namespace - -TEST(PartitionAllocLockTest, AssertAcquiredAnotherThreadHoldsTheLock) { - Lock lock; - // PA_NO_THREAD_SAFETY_ANALYSIS: The checker rightfully points out that the - // lock is still held at the end of the function, which is what we want here. - ThreadDelegateForAssertAcquiredAnotherThreadHoldsTheLock delegate(lock); - base::PlatformThreadHandle handle; - base::PlatformThreadForTesting::Create(0, &delegate, &handle); - // Join before the test, otherwise some platforms' gtest have trouble with - // EXPECT_DEATH() and multiple live threads. - base::PlatformThreadForTesting::Join(handle); - - EXPECT_DEATH(lock.AssertAcquired(), ""); -} - -#if BUILDFLAG(IS_APPLE) - -namespace { - -class ThreadDelegateForReinitInOtherThread - : public base::PlatformThreadForTesting::Delegate { - public: - explicit ThreadDelegateForReinitInOtherThread(Lock& lock) : lock_(lock) {} - - void ThreadMain() PA_NO_THREAD_SAFETY_ANALYSIS override { - lock_.Reinit(); - lock_.Acquire(); - lock_.Release(); - } - - private: - Lock& lock_; -}; - -} // namespace - -// On Apple OSes, it is not allowed to unlock a lock from another thread, so -// we need to re-initialize it. -TEST(PartitionAllocLockTest, ReinitInOtherThread) PA_NO_THREAD_SAFETY_ANALYSIS { - Lock lock; - lock.Acquire(); - - ThreadDelegateForReinitInOtherThread delegate(lock); - base::PlatformThreadHandle handle; - base::PlatformThreadForTesting::Create(0, &delegate, &handle); - base::PlatformThreadForTesting::Join(handle); -} -#endif // BUILDFLAG(IS_APPLE) - -#endif // defined(GTEST_HAS_DEATH_TEST) && BUILDFLAG(PA_DCHECK_IS_ON) - -} // namespace partition_alloc::internal
diff --git a/base/allocator/partition_allocator/partition_oom.cc b/base/allocator/partition_allocator/partition_oom.cc deleted file mode 100644 index cafa119..0000000 --- a/base/allocator/partition_allocator/partition_oom.cc +++ /dev/null
@@ -1,37 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_oom.h" - -#include "base/allocator/partition_allocator/oom.h" -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/alias.h" -#include "build/build_config.h" - -namespace partition_alloc::internal { - -OomFunction g_oom_handling_function = nullptr; - -PA_NOINLINE PA_NOT_TAIL_CALLED void PartitionExcessiveAllocationSize( - size_t size) { - PA_NO_CODE_FOLDING(); - OOM_CRASH(size); -} - -#if !defined(ARCH_CPU_64_BITS) -PA_NOINLINE PA_NOT_TAIL_CALLED void -PartitionOutOfMemoryWithLotsOfUncommitedPages(size_t size) { - PA_NO_CODE_FOLDING(); - OOM_CRASH(size); -} - -[[noreturn]] PA_NOT_TAIL_CALLED PA_NOINLINE void -PartitionOutOfMemoryWithLargeVirtualSize(size_t virtual_size) { - PA_NO_CODE_FOLDING(); - OOM_CRASH(virtual_size); -} - -#endif // !defined(ARCH_CPU_64_BITS) - -} // namespace partition_alloc::internal
diff --git a/base/allocator/partition_allocator/partition_oom.h b/base/allocator/partition_allocator/partition_oom.h deleted file mode 100644 index 085d128..0000000 --- a/base/allocator/partition_allocator/partition_oom.h +++ /dev/null
@@ -1,40 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Holds functions for generating OOM errors from PartitionAlloc. This is -// distinct from oom.h in that it is meant only for use in PartitionAlloc. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_OOM_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_OOM_H_ - -#include <stddef.h> - -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "build/build_config.h" - -namespace partition_alloc { - -using OomFunction = void (*)(size_t); - -namespace internal { - -// g_oom_handling_function is invoked when PartitionAlloc hits OutOfMemory. -extern OomFunction g_oom_handling_function; - -[[noreturn]] PA_NOINLINE PA_COMPONENT_EXPORT( - PARTITION_ALLOC) void PartitionExcessiveAllocationSize(size_t size); - -#if !defined(ARCH_CPU_64_BITS) -[[noreturn]] PA_NOINLINE void PartitionOutOfMemoryWithLotsOfUncommitedPages( - size_t size); -[[noreturn]] PA_NOINLINE void PartitionOutOfMemoryWithLargeVirtualSize( - size_t virtual_size); -#endif - -} // namespace internal - -} // namespace partition_alloc - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_OOM_H_
diff --git a/base/allocator/partition_allocator/partition_page.cc b/base/allocator/partition_allocator/partition_page.cc deleted file mode 100644 index e6d9e2f..0000000 --- a/base/allocator/partition_allocator/partition_page.cc +++ /dev/null
@@ -1,391 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_page.h" - -#include <algorithm> -#include <cstdint> - -#include "base/allocator/partition_allocator/address_pool_manager.h" -#include "base/allocator/partition_allocator/freeslot_bitmap.h" -#include "base/allocator/partition_allocator/page_allocator.h" -#include "base/allocator/partition_allocator/page_allocator_constants.h" -#include "base/allocator/partition_allocator/partition_address_space.h" -#include "base/allocator/partition_allocator/partition_alloc_base/bits.h" -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "base/allocator/partition_allocator/partition_alloc_forward.h" -#include "base/allocator/partition_allocator/partition_direct_map_extent.h" -#include "base/allocator/partition_allocator/partition_root.h" -#include "base/allocator/partition_allocator/reservation_offset_table.h" -#include "base/allocator/partition_allocator/tagging.h" - -namespace partition_alloc::internal { - -namespace { - -void UnmapNow(uintptr_t reservation_start, - size_t reservation_size, - pool_handle pool); - -template <bool thread_safe> -PA_ALWAYS_INLINE void PartitionDirectUnmap( - SlotSpanMetadata<thread_safe>* slot_span) { - auto* root = PartitionRoot<thread_safe>::FromSlotSpan(slot_span); - root->lock_.AssertAcquired(); - auto* extent = PartitionDirectMapExtent<thread_safe>::FromSlotSpan(slot_span); - - // Maintain the doubly-linked list of all direct mappings. - if (extent->prev_extent) { - PA_DCHECK(extent->prev_extent->next_extent == extent); - extent->prev_extent->next_extent = extent->next_extent; - } else { - root->direct_map_list = extent->next_extent; - } - if (extent->next_extent) { - PA_DCHECK(extent->next_extent->prev_extent == extent); - extent->next_extent->prev_extent = extent->prev_extent; - } - - // The actual decommit is deferred below after releasing the lock. - root->DecreaseCommittedPages(slot_span->bucket->slot_size); - - size_t reservation_size = extent->reservation_size; - PA_DCHECK(!(reservation_size & DirectMapAllocationGranularityOffsetMask())); - PA_DCHECK(root->total_size_of_direct_mapped_pages >= reservation_size); - root->total_size_of_direct_mapped_pages -= reservation_size; - - uintptr_t reservation_start = - SlotSpanMetadata<thread_safe>::ToSlotSpanStart(slot_span); - // The mapping may start at an unspecified location within a super page, but - // we always reserve memory aligned to super page size. - reservation_start = base::bits::AlignDown(reservation_start, kSuperPageSize); - - // All the metadata have been updated above, in particular the mapping has - // been unlinked. We can safely release the memory outside the lock, which is - // important as decommitting memory can be expensive. - // - // This can create a fake "address space exhaustion" OOM, in the case where - // e.g. a large allocation is freed on a thread, and another large one is made - // from another *before* UnmapNow() has finished running. In this case the - // second one may not find enough space in the pool, and fail. This is - // expected to be very rare though, and likely preferable to holding the lock - // while releasing the address space. - ScopedUnlockGuard unlock{root->lock_}; - ScopedSyscallTimer timer{root}; - UnmapNow(reservation_start, reservation_size, root->ChoosePool()); -} - -} // namespace - -template <bool thread_safe> -PA_ALWAYS_INLINE void SlotSpanMetadata<thread_safe>::RegisterEmpty() { - PA_DCHECK(is_empty()); - auto* root = PartitionRoot<thread_safe>::FromSlotSpan(this); - root->lock_.AssertAcquired(); - - root->empty_slot_spans_dirty_bytes += - base::bits::AlignUp(GetProvisionedSize(), SystemPageSize()); - - ToSuperPageExtent()->DecrementNumberOfNonemptySlotSpans(); - - // If the slot span is already registered as empty, give it another life. - if (in_empty_cache_) { - PA_DCHECK(empty_cache_index_ < kMaxFreeableSpans); - PA_DCHECK(root->global_empty_slot_span_ring[empty_cache_index_] == this); - root->global_empty_slot_span_ring[empty_cache_index_] = nullptr; - } - - int16_t current_index = root->global_empty_slot_span_ring_index; - SlotSpanMetadata<thread_safe>* slot_span_to_decommit = - root->global_empty_slot_span_ring[current_index]; - // The slot span might well have been re-activated, filled up, etc. before we - // get around to looking at it here. - if (slot_span_to_decommit) { - slot_span_to_decommit->DecommitIfPossible(root); - } - - // We put the empty slot span on our global list of "slot spans that were once - // empty", thus providing it a bit of breathing room to get re-used before we - // really free it. This reduces the number of system calls. Otherwise any - // free() from a single-slot slot span would lead to a syscall, for instance. - root->global_empty_slot_span_ring[current_index] = this; - empty_cache_index_ = current_index; - in_empty_cache_ = 1; - ++current_index; - if (current_index == root->global_empty_slot_span_ring_size) { - current_index = 0; - } - root->global_empty_slot_span_ring_index = current_index; - - // Avoid wasting too much memory on empty slot spans. Note that we only divide - // by powers of two, since division can be very slow, and this path is taken - // for every single-slot slot span deallocation. - // - // Empty slot spans are also all decommitted with MemoryReclaimer, but it may - // never run, be delayed arbitrarily, and/or miss large memory spikes. - size_t max_empty_dirty_bytes = - root->total_size_of_committed_pages.load(std::memory_order_relaxed) >> - root->max_empty_slot_spans_dirty_bytes_shift; - if (root->empty_slot_spans_dirty_bytes > max_empty_dirty_bytes) { - root->ShrinkEmptySlotSpansRing(std::min( - root->empty_slot_spans_dirty_bytes / 2, max_empty_dirty_bytes)); - } -} -// static -template <bool thread_safe> -const SlotSpanMetadata<thread_safe> - SlotSpanMetadata<thread_safe>::sentinel_slot_span_; - -// static -template <bool thread_safe> -const SlotSpanMetadata<thread_safe>* -SlotSpanMetadata<thread_safe>::get_sentinel_slot_span() { - return &sentinel_slot_span_; -} - -// static -template <bool thread_safe> -SlotSpanMetadata<thread_safe>* -SlotSpanMetadata<thread_safe>::get_sentinel_slot_span_non_const() { - return const_cast<SlotSpanMetadata<thread_safe>*>(&sentinel_slot_span_); -} - -template <bool thread_safe> -SlotSpanMetadata<thread_safe>::SlotSpanMetadata( - PartitionBucket<thread_safe>* bucket) - : bucket(bucket), can_store_raw_size_(bucket->CanStoreRawSize()) {} - -template <bool thread_safe> -void SlotSpanMetadata<thread_safe>::FreeSlowPath(size_t number_of_freed) { -#if BUILDFLAG(PA_DCHECK_IS_ON) - auto* root = PartitionRoot<thread_safe>::FromSlotSpan(this); - root->lock_.AssertAcquired(); -#endif - PA_DCHECK(this != get_sentinel_slot_span()); - - // The caller has already modified |num_allocated_slots|. It is a - // responsibility of this function to react to it, and update the state. We - // can get here only if the slot span is marked full and/or is now empty. Both - // are possible at the same time, which can happen when the caller lowered - // |num_allocated_slots| from "all" to 0 (common for single-slot spans). First - // execute the "is marked full" path, as it sets up |active_slot_spans_head| - // in a way later needed for the "is empty" path. - if (marked_full) { - // Direct map slot spans aren't added to any lists, hence never marked full. - PA_DCHECK(!bucket->is_direct_mapped()); - // Double check that the slot span was full. - PA_DCHECK(num_allocated_slots == - bucket->get_slots_per_span() - number_of_freed); - marked_full = 0; - // Fully used slot span became partially used. It must be put back on the - // non-full list. Also make it the current slot span to increase the - // chances of it being filled up again. The old current slot span will be - // the next slot span. - PA_DCHECK(!next_slot_span); - if (PA_LIKELY(bucket->active_slot_spans_head != get_sentinel_slot_span())) { - next_slot_span = bucket->active_slot_spans_head; - } - bucket->active_slot_spans_head = this; - PA_CHECK(bucket->num_full_slot_spans); // Underflow. - --bucket->num_full_slot_spans; - } - - if (PA_LIKELY(num_allocated_slots == 0)) { - // Slot span became fully unused. - if (PA_UNLIKELY(bucket->is_direct_mapped())) { - PartitionDirectUnmap(this); - return; - } -#if BUILDFLAG(PA_DCHECK_IS_ON) - freelist_head->CheckFreeList(bucket->slot_size); -#endif - // If it's the current active slot span, change it. We bounce the slot span - // to the empty list as a force towards defragmentation. - if (PA_LIKELY(this == bucket->active_slot_spans_head)) { - bucket->SetNewActiveSlotSpan(); - } - PA_DCHECK(bucket->active_slot_spans_head != this); - - if (CanStoreRawSize()) { - SetRawSize(0); - } - - RegisterEmpty(); - } -} - -template <bool thread_safe> -void SlotSpanMetadata<thread_safe>::Decommit(PartitionRoot<thread_safe>* root) { - root->lock_.AssertAcquired(); - PA_DCHECK(is_empty()); - PA_DCHECK(!bucket->is_direct_mapped()); - uintptr_t slot_span_start = SlotSpanMetadata::ToSlotSpanStart(this); - // If lazy commit is enabled, only provisioned slots are committed. - size_t dirty_size = - base::bits::AlignUp(GetProvisionedSize(), SystemPageSize()); - size_t size_to_decommit = - kUseLazyCommit ? dirty_size : bucket->get_bytes_per_span(); - - PA_DCHECK(root->empty_slot_spans_dirty_bytes >= dirty_size); - root->empty_slot_spans_dirty_bytes -= dirty_size; - - // Not decommitted slot span must've had at least 1 allocation. - PA_DCHECK(size_to_decommit > 0); - root->DecommitSystemPagesForData( - slot_span_start, size_to_decommit, - PageAccessibilityDisposition::kAllowKeepForPerf); - -#if BUILDFLAG(USE_FREESLOT_BITMAP) - FreeSlotBitmapReset(slot_span_start, slot_span_start + size_to_decommit, - bucket->slot_size); -#endif - - // We actually leave the decommitted slot span in the active list. We'll sweep - // it on to the decommitted list when we next walk the active list. - // Pulling this trick enables us to use a singly-linked list for all - // cases, which is critical in keeping the slot span metadata structure down - // to 32 bytes in size. - SetFreelistHead(nullptr); - num_unprovisioned_slots = 0; - PA_DCHECK(is_decommitted()); - PA_DCHECK(bucket); -} - -template <bool thread_safe> -void SlotSpanMetadata<thread_safe>::DecommitIfPossible( - PartitionRoot<thread_safe>* root) { - root->lock_.AssertAcquired(); - PA_DCHECK(in_empty_cache_); - PA_DCHECK(empty_cache_index_ < kMaxFreeableSpans); - PA_DCHECK(this == root->global_empty_slot_span_ring[empty_cache_index_]); - in_empty_cache_ = 0; - if (is_empty()) { - Decommit(root); - } -} - -template <bool thread_safe> -void SlotSpanMetadata<thread_safe>::SortFreelist() { - std::bitset<kMaxSlotsPerSlotSpan> free_slots; - uintptr_t slot_span_start = ToSlotSpanStart(this); - - size_t num_provisioned_slots = - bucket->get_slots_per_span() - num_unprovisioned_slots; - PA_CHECK(num_provisioned_slots <= kMaxSlotsPerSlotSpan); - - size_t num_free_slots = 0; - size_t slot_size = bucket->slot_size; - for (PartitionFreelistEntry* head = freelist_head; head; - head = head->GetNext(slot_size)) { - ++num_free_slots; - size_t offset_in_slot_span = SlotStartPtr2Addr(head) - slot_span_start; - size_t slot_number = bucket->GetSlotNumber(offset_in_slot_span); - PA_DCHECK(slot_number < num_provisioned_slots); - free_slots[slot_number] = true; - } - PA_DCHECK(num_free_slots == GetFreelistLength()); - - // Empty or single-element list is always sorted. - if (num_free_slots > 1) { - PartitionFreelistEntry* back = nullptr; - PartitionFreelistEntry* head = nullptr; - - for (size_t slot_number = 0; slot_number < num_provisioned_slots; - slot_number++) { - if (free_slots[slot_number]) { - uintptr_t slot_start = slot_span_start + (slot_size * slot_number); - auto* entry = PartitionFreelistEntry::EmplaceAndInitNull(slot_start); - - if (!head) { - head = entry; - } else { - back->SetNext(entry); - } - - back = entry; - } - } - SetFreelistHead(head); - } - - freelist_is_sorted_ = true; -} - -namespace { - -void UnmapNow(uintptr_t reservation_start, - size_t reservation_size, - pool_handle pool) { - PA_DCHECK(reservation_start && reservation_size > 0); -#if BUILDFLAG(PA_DCHECK_IS_ON) - // When ENABLE_BACKUP_REF_PTR_SUPPORT is off, BRP pool isn't used. -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - if (pool == kBRPPoolHandle) { - // In 32-bit mode, the beginning of a reservation may be excluded from the - // BRP pool, so shift the pointer. Other pools don't have this logic. - PA_DCHECK(IsManagedByPartitionAllocBRPPool( -#if BUILDFLAG(HAS_64_BIT_POINTERS) - reservation_start -#else - reservation_start + - AddressPoolManagerBitmap::kBytesPer1BitOfBRPPoolBitmap * - AddressPoolManagerBitmap::kGuardOffsetOfBRPPoolBitmap -#endif // BUILDFLAG(HAS_64_BIT_POINTERS) - )); - } else -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - { - PA_DCHECK(pool == kRegularPoolHandle -#if BUILDFLAG(ENABLE_PKEYS) - || pool == kPkeyPoolHandle -#endif -#if BUILDFLAG(HAS_64_BIT_POINTERS) - || - (IsConfigurablePoolAvailable() && pool == kConfigurablePoolHandle) -#endif - ); - // Non-BRP pools don't need adjustment that BRP needs in 32-bit mode. - PA_DCHECK(IsManagedByPartitionAllocRegularPool(reservation_start) || -#if BUILDFLAG(ENABLE_PKEYS) - IsManagedByPartitionAllocPkeyPool(reservation_start) || -#endif - IsManagedByPartitionAllocConfigurablePool(reservation_start)); - } -#endif // BUILDFLAG(PA_DCHECK_IS_ON) - - PA_DCHECK((reservation_start & kSuperPageOffsetMask) == 0); - uintptr_t reservation_end = reservation_start + reservation_size; - auto* offset_ptr = ReservationOffsetPointer(reservation_start); - // Reset the offset table entries for the given memory before unreserving - // it. Since the memory is not unreserved and not available for other - // threads, the table entries for the memory are not modified by other - // threads either. So we can update the table entries without race - // condition. - uint16_t i = 0; - for (uintptr_t address = reservation_start; address < reservation_end; - address += kSuperPageSize) { - PA_DCHECK(offset_ptr < GetReservationOffsetTableEnd(address)); - PA_DCHECK(*offset_ptr == i++); - *offset_ptr++ = kOffsetTagNotAllocated; - } - -#if !BUILDFLAG(HAS_64_BIT_POINTERS) - AddressPoolManager::GetInstance().MarkUnused(pool, reservation_start, - reservation_size); -#endif - - // After resetting the table entries, unreserve and decommit the memory. - AddressPoolManager::GetInstance().UnreserveAndDecommit( - pool, reservation_start, reservation_size); -} - -} // namespace - -template struct SlotSpanMetadata<ThreadSafe>; - -} // namespace partition_alloc::internal
diff --git a/base/allocator/partition_allocator/partition_page.h b/base/allocator/partition_allocator/partition_page.h deleted file mode 100644 index 6090844..0000000 --- a/base/allocator/partition_allocator/partition_page.h +++ /dev/null
@@ -1,945 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_PAGE_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_PAGE_H_ - -#include <cstdint> -#include <cstring> -#include <limits> -#include <utility> - -#include "base/allocator/partition_allocator/address_pool_manager.h" -#include "base/allocator/partition_allocator/address_pool_manager_types.h" -#include "base/allocator/partition_allocator/freeslot_bitmap_constants.h" -#include "base/allocator/partition_allocator/partition_address_space.h" -#include "base/allocator/partition_allocator/partition_alloc_base/bits.h" -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_base/thread_annotations.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "base/allocator/partition_allocator/partition_alloc_forward.h" -#include "base/allocator/partition_allocator/partition_bucket.h" -#include "base/allocator/partition_allocator/partition_freelist_entry.h" -#include "base/allocator/partition_allocator/reservation_offset_table.h" -#include "base/allocator/partition_allocator/tagging.h" -#include "build/build_config.h" - -#if BUILDFLAG(USE_STARSCAN) -#include "base/allocator/partition_allocator/starscan/state_bitmap.h" -#endif - -#if BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) -#include "base/allocator/partition_allocator/partition_ref_count.h" -#endif - -namespace partition_alloc::internal { - -// An "extent" is a span of consecutive superpages. We link the partition's next -// extent (if there is one) to the very start of a superpage's metadata area. -template <bool thread_safe> -struct PartitionSuperPageExtentEntry { - PartitionRoot<thread_safe>* root; - PartitionSuperPageExtentEntry<thread_safe>* next; - uint16_t number_of_consecutive_super_pages; - uint16_t number_of_nonempty_slot_spans; - - PA_ALWAYS_INLINE void IncrementNumberOfNonemptySlotSpans(); - PA_ALWAYS_INLINE void DecrementNumberOfNonemptySlotSpans(); -}; -static_assert( - sizeof(PartitionSuperPageExtentEntry<ThreadSafe>) <= kPageMetadataSize, - "PartitionSuperPageExtentEntry must be able to fit in a metadata slot"); -static_assert( - kMaxSuperPagesInPool / kSuperPageSize <= - std::numeric_limits< - decltype(PartitionSuperPageExtentEntry< - ThreadSafe>::number_of_consecutive_super_pages)>::max(), - "number_of_consecutive_super_pages must be big enough"); - -// Returns the base of the first super page in the range of consecutive super -// pages. -// -// CAUTION! |extent| must point to the extent of the first super page in the -// range of consecutive super pages. -template <bool thread_safe> -PA_ALWAYS_INLINE uintptr_t SuperPagesBeginFromExtent( - const PartitionSuperPageExtentEntry<thread_safe>* extent) { - PA_DCHECK(0 < extent->number_of_consecutive_super_pages); - uintptr_t extent_as_uintptr = reinterpret_cast<uintptr_t>(extent); - PA_DCHECK(IsManagedByNormalBuckets(extent_as_uintptr)); - return base::bits::AlignDown(extent_as_uintptr, kSuperPageAlignment); -} - -// Returns the end of the last super page in the range of consecutive super -// pages. -// -// CAUTION! |extent| must point to the extent of the first super page in the -// range of consecutive super pages. -template <bool thread_safe> -PA_ALWAYS_INLINE uintptr_t SuperPagesEndFromExtent( - const PartitionSuperPageExtentEntry<thread_safe>* extent) { - return SuperPagesBeginFromExtent(extent) + - (extent->number_of_consecutive_super_pages * kSuperPageSize); -} - -#if BUILDFLAG(USE_STARSCAN) -using AllocationStateMap = - StateBitmap<kSuperPageSize, kSuperPageAlignment, kAlignment>; -#endif - -// Metadata of the slot span. -// -// Some notes on slot span states. It can be in one of four major states: -// 1) Active. -// 2) Full. -// 3) Empty. -// 4) Decommitted. -// An active slot span has available free slots, as well as allocated ones. -// A full slot span has no free slots. An empty slot span has no allocated -// slots, and a decommitted slot span is an empty one that had its backing -// memory released back to the system. -// -// There are three linked lists tracking slot spans. The "active" list is an -// approximation of a list of active slot spans. It is an approximation because -// full, empty and decommitted slot spans may briefly be present in the list -// until we next do a scan over it. The "empty" list holds mostly empty slot -// spans, but may briefly hold decommitted ones too. The "decommitted" list -// holds only decommitted slot spans. -// -// The significant slot span transitions are: -// - Free() will detect when a full slot span has a slot freed and immediately -// return the slot span to the head of the active list. -// - Free() will detect when a slot span is fully emptied. It _may_ add it to -// the empty list or it _may_ leave it on the active list until a future -// list scan. -// - Alloc() _may_ scan the active page list in order to fulfil the request. -// If it does this, full, empty and decommitted slot spans encountered will be -// booted out of the active list. If there are no suitable active slot spans -// found, an empty or decommitted slot spans (if one exists) will be pulled -// from the empty/decommitted list on to the active list. -#pragma pack(push, 1) -template <bool thread_safe> -struct SlotSpanMetadata { - private: - PartitionFreelistEntry* freelist_head = nullptr; - - public: - // TODO(lizeb): Make as many fields as possible private or const, to - // encapsulate things more clearly. - SlotSpanMetadata<thread_safe>* next_slot_span = nullptr; - PartitionBucket<thread_safe>* const bucket = nullptr; - - // CHECK()ed in AllocNewSlotSpan(). -#if BUILDFLAG(HAS_64_BIT_POINTERS) && BUILDFLAG(IS_APPLE) - // System page size is not a constant on Apple OSes, but is either 4 or 16kiB - // (1 << 12 or 1 << 14), as checked in PartitionRoot::Init(). And - // PartitionPageSize() is 4 times the OS page size. - static constexpr size_t kMaxSlotsPerSlotSpan = - 4 * (1 << 14) / kSmallestBucket; -#elif BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64) - // System page size can be 4, 16, or 64 kiB on Linux on arm64. 64 kiB is - // currently (kMaxSlotsPerSlotSpanBits == 13) not supported by the code, - // so we use the 16 kiB maximum (64 kiB will crash). - static constexpr size_t kMaxSlotsPerSlotSpan = - 4 * (1 << 14) / kSmallestBucket; -#else - // A slot span can "span" multiple PartitionPages, but then its slot size is - // larger, so it doesn't have as many slots. - static constexpr size_t kMaxSlotsPerSlotSpan = - PartitionPageSize() / kSmallestBucket; -#endif // BUILDFLAG(HAS_64_BIT_POINTERS) && BUILDFLAG(IS_APPLE) - // The maximum number of bits needed to cover all currently supported OSes. - static constexpr size_t kMaxSlotsPerSlotSpanBits = 13; - static_assert(kMaxSlotsPerSlotSpan < (1 << kMaxSlotsPerSlotSpanBits), ""); - - // |marked_full| isn't equivalent to being full. Slot span is marked as full - // iff it isn't on the active slot span list (or any other list). - uint32_t marked_full : 1; - // |num_allocated_slots| is 0 for empty or decommitted slot spans, which can - // be further differentiated by checking existence of the freelist. - uint32_t num_allocated_slots : kMaxSlotsPerSlotSpanBits; - uint32_t num_unprovisioned_slots : kMaxSlotsPerSlotSpanBits; - - private: - const uint32_t can_store_raw_size_ : 1; - uint32_t freelist_is_sorted_ : 1; - uint32_t unused1_ : (32 - 1 - 2 * kMaxSlotsPerSlotSpanBits - 1 - 1); - // If |in_empty_cache_|==1, |empty_cache_index| is undefined and mustn't be - // used. - uint16_t in_empty_cache_ : 1; - uint16_t empty_cache_index_ : kEmptyCacheIndexBits; // < kMaxFreeableSpans. - uint16_t unused2_ : (16 - 1 - kEmptyCacheIndexBits); - // Can use only 48 bits (6B) in this bitfield, as this structure is embedded - // in PartitionPage which has 2B worth of fields and must fit in 32B. - - public: - PA_COMPONENT_EXPORT(PARTITION_ALLOC) - explicit SlotSpanMetadata(PartitionBucket<thread_safe>* bucket); - - // Public API - // Note the matching Alloc() functions are in PartitionPage. - PA_NOINLINE PA_COMPONENT_EXPORT(PARTITION_ALLOC) void FreeSlowPath( - size_t number_of_freed); - PA_ALWAYS_INLINE PartitionFreelistEntry* PopForAlloc(size_t size); - PA_ALWAYS_INLINE void Free(uintptr_t ptr); - // Appends the passed freelist to the slot-span's freelist. Please note that - // the function doesn't increment the tags of the passed freelist entries, - // since FreeNoHooks() did it already. - PA_ALWAYS_INLINE void AppendFreeList(PartitionFreelistEntry* head, - PartitionFreelistEntry* tail, - size_t number_of_freed); - - void Decommit(PartitionRoot<thread_safe>* root); - void DecommitIfPossible(PartitionRoot<thread_safe>* root); - - // Sorts the freelist in ascending addresses order. - void SortFreelist(); - // Inserts the slot span into the empty ring, making space for the new slot - // span, and potentially shrinking the ring. - void RegisterEmpty(); - - // Pointer/address manipulation functions. These must be static as the input - // |slot_span| pointer may be the result of an offset calculation and - // therefore cannot be trusted. The objective of these functions is to - // sanitize this input. - PA_ALWAYS_INLINE static uintptr_t ToSlotSpanStart( - const SlotSpanMetadata* slot_span); - PA_ALWAYS_INLINE static SlotSpanMetadata* FromAddr(uintptr_t address); - PA_ALWAYS_INLINE static SlotSpanMetadata* FromSlotStart(uintptr_t slot_start); - PA_ALWAYS_INLINE static SlotSpanMetadata* FromObject(void* object); - PA_ALWAYS_INLINE static SlotSpanMetadata* FromObjectInnerAddr( - uintptr_t address); - PA_ALWAYS_INLINE static SlotSpanMetadata* FromObjectInnerPtr(void* ptr); - - PA_ALWAYS_INLINE PartitionSuperPageExtentEntry<thread_safe>* - ToSuperPageExtent() const; - - // Checks if it is feasible to store raw_size. - PA_ALWAYS_INLINE bool CanStoreRawSize() const { return can_store_raw_size_; } - // The caller is responsible for ensuring that raw_size can be stored before - // calling Set/GetRawSize. - PA_ALWAYS_INLINE void SetRawSize(size_t raw_size); - PA_ALWAYS_INLINE size_t GetRawSize() const; - - PA_ALWAYS_INLINE PartitionFreelistEntry* get_freelist_head() const { - return freelist_head; - } - PA_ALWAYS_INLINE void SetFreelistHead(PartitionFreelistEntry* new_head); - - // Returns size of the region used within a slot. The used region comprises - // of actual allocated data, extras and possibly empty space in the middle. - PA_ALWAYS_INLINE size_t GetUtilizedSlotSize() const { - // The returned size can be: - // - The slot size for small buckets. - // - Exact size needed to satisfy allocation (incl. extras), for large - // buckets and direct-mapped allocations (see also the comment in - // CanStoreRawSize() for more info). - if (PA_LIKELY(!CanStoreRawSize())) { - return bucket->slot_size; - } - return GetRawSize(); - } - - // This includes padding due to rounding done at allocation; we don't know the - // requested size at deallocation, so we use this in both places. - PA_ALWAYS_INLINE size_t GetSlotSizeForBookkeeping() const { - // This could be more precise for allocations where CanStoreRawSize() - // returns true (large allocations). However this is called for *every* - // allocation, so we don't want an extra branch there. - return bucket->slot_size; - } - - // Returns the size available to the app. It can be equal or higher than the - // requested size. If higher, the overage won't exceed what's actually usable - // by the app without a risk of running out of an allocated region or into - // PartitionAlloc's internal data (like extras). - PA_ALWAYS_INLINE size_t - GetUsableSize(PartitionRoot<thread_safe>* root) const { - // The returned size can be: - // - The slot size minus extras, for small buckets. This could be more than - // requested size. - // - Raw size minus extras, for large buckets and direct-mapped allocations - // (see also the comment in CanStoreRawSize() for more info). This is - // equal to requested size. - return root->AdjustSizeForExtrasSubtract(GetUtilizedSlotSize()); - } - - // Returns the total size of the slots that are currently provisioned. - PA_ALWAYS_INLINE size_t GetProvisionedSize() const { - size_t num_provisioned_slots = - bucket->get_slots_per_span() - num_unprovisioned_slots; - size_t provisioned_size = num_provisioned_slots * bucket->slot_size; - PA_DCHECK(provisioned_size <= bucket->get_bytes_per_span()); - return provisioned_size; - } - - // Return the number of entries in the freelist. - size_t GetFreelistLength() const { - size_t num_provisioned_slots = - bucket->get_slots_per_span() - num_unprovisioned_slots; - return num_provisioned_slots - num_allocated_slots; - } - - PA_ALWAYS_INLINE void Reset(); - - // TODO(ajwong): Can this be made private? https://crbug.com/787153 - PA_COMPONENT_EXPORT(PARTITION_ALLOC) - static const SlotSpanMetadata* get_sentinel_slot_span(); - // The sentinel is not supposed to be modified and hence we mark it as const - // under the hood. However, we often store it together with mutable metadata - // objects and need a non-const pointer. - // You can use this function for this case, but you need to ensure that the - // returned object will not be written to. - static SlotSpanMetadata* get_sentinel_slot_span_non_const(); - - // Slot span state getters. - PA_ALWAYS_INLINE bool is_active() const; - PA_ALWAYS_INLINE bool is_full() const; - PA_ALWAYS_INLINE bool is_empty() const; - PA_ALWAYS_INLINE bool is_decommitted() const; - PA_ALWAYS_INLINE bool in_empty_cache() const { return in_empty_cache_; } - PA_ALWAYS_INLINE bool freelist_is_sorted() const { - return freelist_is_sorted_; - } - PA_ALWAYS_INLINE void set_freelist_sorted() { freelist_is_sorted_ = true; } - - private: - // sentinel_slot_span_ is used as a sentinel to indicate that there is no slot - // span in the active list. We could use nullptr, but in that case we need to - // add a null-check branch to the hot allocation path. We want to avoid that. - // - // Note, this declaration is kept in the header as opposed to an anonymous - // namespace so the getter can be fully inlined. - static const SlotSpanMetadata sentinel_slot_span_; - // For the sentinel. - constexpr SlotSpanMetadata() noexcept - : marked_full(0), - num_allocated_slots(0), - num_unprovisioned_slots(0), - can_store_raw_size_(false), - freelist_is_sorted_(true), - unused1_(0), - in_empty_cache_(0), - empty_cache_index_(0), - unused2_(0) {} -}; -#pragma pack(pop) -static_assert(sizeof(SlotSpanMetadata<ThreadSafe>) <= kPageMetadataSize, - "SlotSpanMetadata must fit into a Page Metadata slot."); - -// Metadata of a non-first partition page in a slot span. -struct SubsequentPageMetadata { - // Raw size is the size needed to satisfy the allocation (requested size + - // extras). If available, it can be used to report better statistics or to - // bring protective cookie closer to the allocated memory. - // - // It can be used only if: - // - there is no more than one slot in the slot span (otherwise we wouldn't - // know which slot the raw size applies to) - // - there is more than one partition page in the slot span (the metadata of - // the first one is used to store slot information, but the second one is - // available for extra information) - size_t raw_size; -}; - -// Each partition page has metadata associated with it. The metadata of the -// first page of a slot span, describes that slot span. If a slot span spans -// more than 1 page, the page metadata may contain rudimentary additional -// information. -// "Pack" the union so that common page metadata still fits within -// kPageMetadataSize. (SlotSpanMetadata is also "packed".) -#pragma pack(push, 1) -template <bool thread_safe> -struct PartitionPage { - union { - SlotSpanMetadata<thread_safe> slot_span_metadata; - - SubsequentPageMetadata subsequent_page_metadata; - - // sizeof(PartitionPageMetadata) must always be: - // - a power of 2 (for fast modulo operations) - // - below kPageMetadataSize - // - // This makes sure that this is respected no matter the architecture. - char optional_padding[kPageMetadataSize - sizeof(uint8_t) - sizeof(bool)]; - }; - - // The first PartitionPage of the slot span holds its metadata. This offset - // tells how many pages in from that first page we are. - // For direct maps, the first page metadata (that isn't super page extent - // entry) uses this field to tell how many pages to the right the direct map - // metadata starts. - // - // 6 bits is enough to represent all possible offsets, given that the smallest - // partition page is 16kiB and the offset won't exceed 1MiB. - static constexpr uint16_t kMaxSlotSpanMetadataBits = 6; - static constexpr uint16_t kMaxSlotSpanMetadataOffset = - (1 << kMaxSlotSpanMetadataBits) - 1; - uint8_t slot_span_metadata_offset : kMaxSlotSpanMetadataBits; - - // |is_valid| tells whether the page is part of a slot span. If |false|, - // |has_valid_span_after_this| tells whether it's an unused region in between - // slot spans within the super page. - // Note, |is_valid| has been added for clarity, but if we ever need to save - // this bit, it can be inferred from: - // |!slot_span_metadata_offset && slot_span_metadata->bucket|. - bool is_valid : 1; - bool has_valid_span_after_this : 1; - uint8_t unused; - - PA_ALWAYS_INLINE static PartitionPage* FromAddr(uintptr_t address); -}; -#pragma pack(pop) -static_assert(sizeof(PartitionPage<ThreadSafe>) == kPageMetadataSize, - "PartitionPage must be able to fit in a metadata slot"); - -// Certain functions rely on PartitionPage being either SlotSpanMetadata or -// SubsequentPageMetadata, and therefore freely casting between each other. -static_assert(offsetof(PartitionPage<ThreadSafe>, slot_span_metadata) == 0, ""); -static_assert(offsetof(PartitionPage<ThreadSafe>, subsequent_page_metadata) == - 0, - ""); - -template <bool thread_safe> -PA_ALWAYS_INLINE PartitionPage<thread_safe>* PartitionSuperPageToMetadataArea( - uintptr_t super_page) { - // This can't be just any super page, but it has to be the first super page of - // the reservation, as we assume here that the metadata is near its beginning. - PA_DCHECK(IsReservationStart(super_page)); - PA_DCHECK(!(super_page & kSuperPageOffsetMask)); - // The metadata area is exactly one system page (the guard page) into the - // super page. - return reinterpret_cast<PartitionPage<thread_safe>*>(super_page + - SystemPageSize()); -} - -PA_ALWAYS_INLINE const SubsequentPageMetadata* GetSubsequentPageMetadata( - const PartitionPage<ThreadSafe>* page) { - return &(page + 1)->subsequent_page_metadata; -} - -PA_ALWAYS_INLINE SubsequentPageMetadata* GetSubsequentPageMetadata( - PartitionPage<ThreadSafe>* page) { - return &(page + 1)->subsequent_page_metadata; -} - -template <bool thread_safe> -PA_ALWAYS_INLINE PartitionSuperPageExtentEntry<thread_safe>* -PartitionSuperPageToExtent(uintptr_t super_page) { - // The very first entry of the metadata is the super page extent entry. - return reinterpret_cast<PartitionSuperPageExtentEntry<thread_safe>*>( - PartitionSuperPageToMetadataArea<thread_safe>(super_page)); -} - -#if BUILDFLAG(USE_STARSCAN) - -// Size that should be reserved for state bitmap (if present) inside a super -// page. Elements of a super page are partition-page-aligned, hence the returned -// size is a multiple of partition page size. -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t -ReservedStateBitmapSize() { - return base::bits::AlignUp(sizeof(AllocationStateMap), PartitionPageSize()); -} - -// Size that should be committed for state bitmap (if present) inside a super -// page. It is a multiple of system page size. -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t -CommittedStateBitmapSize() { - return base::bits::AlignUp(sizeof(AllocationStateMap), SystemPageSize()); -} - -// Returns the address/pointer to the state bitmap in the super page. It's the -// caller's responsibility to ensure that the bitmaps even exist. -PA_ALWAYS_INLINE uintptr_t SuperPageStateBitmapAddr(uintptr_t super_page) { - PA_DCHECK(!(super_page % kSuperPageAlignment)); - return super_page + PartitionPageSize() + - (IsManagedByNormalBuckets(super_page) ? ReservedFreeSlotBitmapSize() - : 0); -} - -PA_ALWAYS_INLINE AllocationStateMap* SuperPageStateBitmap( - uintptr_t super_page) { - return reinterpret_cast<AllocationStateMap*>( - SuperPageStateBitmapAddr(super_page)); -} - -#else // BUILDFLAG(USE_STARSCAN) - -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t -ReservedStateBitmapSize() { - return 0ull; -} - -#endif // BUILDFLAG(USE_STARSCAN) - -PA_ALWAYS_INLINE uintptr_t -SuperPagePayloadStartOffset(bool is_managed_by_normal_buckets, - bool with_quarantine) { - return PartitionPageSize() + - (is_managed_by_normal_buckets ? ReservedFreeSlotBitmapSize() : 0) + - (with_quarantine ? ReservedStateBitmapSize() : 0); -} - -PA_ALWAYS_INLINE uintptr_t SuperPagePayloadBegin(uintptr_t super_page, - bool with_quarantine) { - PA_DCHECK(!(super_page % kSuperPageAlignment)); - return super_page + - SuperPagePayloadStartOffset(IsManagedByNormalBuckets(super_page), - with_quarantine); -} - -PA_ALWAYS_INLINE uintptr_t SuperPagePayloadEndOffset() { - return kSuperPageSize - PartitionPageSize(); -} - -PA_ALWAYS_INLINE uintptr_t SuperPagePayloadEnd(uintptr_t super_page) { - PA_DCHECK(!(super_page % kSuperPageAlignment)); - return super_page + SuperPagePayloadEndOffset(); -} - -PA_ALWAYS_INLINE size_t SuperPagePayloadSize(uintptr_t super_page, - bool with_quarantine) { - return SuperPagePayloadEnd(super_page) - - SuperPagePayloadBegin(super_page, with_quarantine); -} - -template <bool thread_safe> -PA_ALWAYS_INLINE void PartitionSuperPageExtentEntry< - thread_safe>::IncrementNumberOfNonemptySlotSpans() { -#if BUILDFLAG(PA_DCHECK_IS_ON) - uintptr_t super_page = base::bits::AlignDown( - reinterpret_cast<uintptr_t>(this), kSuperPageAlignment); - PA_DCHECK((SuperPagePayloadSize(super_page, root->IsQuarantineAllowed()) / - PartitionPageSize()) > number_of_nonempty_slot_spans); -#endif - ++number_of_nonempty_slot_spans; -} - -template <bool thread_safe> -PA_ALWAYS_INLINE void PartitionSuperPageExtentEntry< - thread_safe>::DecrementNumberOfNonemptySlotSpans() { - PA_DCHECK(number_of_nonempty_slot_spans); - --number_of_nonempty_slot_spans; -} - -template <bool thread_safe> -PA_ALWAYS_INLINE PartitionSuperPageExtentEntry<thread_safe>* -SlotSpanMetadata<thread_safe>::ToSuperPageExtent() const { - uintptr_t super_page = reinterpret_cast<uintptr_t>(this) & kSuperPageBaseMask; - return PartitionSuperPageToExtent<thread_safe>(super_page); -} - -// Returns whether the pointer lies within the super page's payload area (i.e. -// area devoted to slot spans). It doesn't check whether it's within a valid -// slot span. It merely ensures it doesn't fall in a meta-data region that would -// surely never contain user data. -PA_ALWAYS_INLINE bool IsWithinSuperPagePayload(uintptr_t address, - bool with_quarantine) { - // Quarantine can only be enabled for normal buckets in the current code. - PA_DCHECK(!with_quarantine || IsManagedByNormalBuckets(address)); - uintptr_t super_page = address & kSuperPageBaseMask; - uintptr_t payload_start = SuperPagePayloadBegin(super_page, with_quarantine); - uintptr_t payload_end = SuperPagePayloadEnd(super_page); - return address >= payload_start && address < payload_end; -} - -// Converts from an address inside a super page into a pointer to the -// PartitionPage object (within super pages's metadata) that describes the -// partition page where |address| is located. |address| doesn't have to be -// located within a valid (i.e. allocated) slot span, but must be within the -// super page's payload area (i.e. area devoted to slot spans). -// -// While it is generally valid for |ptr| to be in the middle of an allocation, -// care has to be taken with direct maps that span multiple super pages. This -// function's behavior is undefined if |ptr| lies in a subsequent super page. -template <bool thread_safe> -PA_ALWAYS_INLINE PartitionPage<thread_safe>* -PartitionPage<thread_safe>::FromAddr(uintptr_t address) { - uintptr_t super_page = address & kSuperPageBaseMask; - -#if BUILDFLAG(PA_DCHECK_IS_ON) - PA_DCHECK(IsReservationStart(super_page)); - auto* extent = PartitionSuperPageToExtent<thread_safe>(super_page); - PA_DCHECK(IsWithinSuperPagePayload(address, - IsManagedByNormalBuckets(address) && - extent->root->IsQuarantineAllowed())); -#endif - - uintptr_t partition_page_index = - (address & kSuperPageOffsetMask) >> PartitionPageShift(); - // Index 0 is invalid because it is the super page extent metadata and the - // last index is invalid because the whole PartitionPage is set as guard - // pages. This repeats part of the payload PA_DCHECK above, which also checks - // for other exclusions. - PA_DCHECK(partition_page_index); - PA_DCHECK(partition_page_index < NumPartitionPagesPerSuperPage() - 1); - return PartitionSuperPageToMetadataArea<thread_safe>(super_page) + - partition_page_index; -} - -// Converts from a pointer to the SlotSpanMetadata object (within a super -// pages's metadata) into a pointer to the beginning of the slot span. This -// works on direct maps too. -template <bool thread_safe> -PA_ALWAYS_INLINE uintptr_t SlotSpanMetadata<thread_safe>::ToSlotSpanStart( - const SlotSpanMetadata* slot_span) { - uintptr_t pointer_as_uint = reinterpret_cast<uintptr_t>(slot_span); - uintptr_t super_page_offset = (pointer_as_uint & kSuperPageOffsetMask); - - // A valid |page| must be past the first guard System page and within - // the following metadata region. - PA_DCHECK(super_page_offset > SystemPageSize()); - // Must be less than total metadata region. - PA_DCHECK(super_page_offset < - SystemPageSize() + - (NumPartitionPagesPerSuperPage() * kPageMetadataSize)); - uintptr_t partition_page_index = - (super_page_offset - SystemPageSize()) >> kPageMetadataShift; - // Index 0 is invalid because it is the super page extent metadata and the - // last index is invalid because the whole PartitionPage is set as guard - // pages. - PA_DCHECK(partition_page_index); - PA_DCHECK(partition_page_index < NumPartitionPagesPerSuperPage() - 1); - uintptr_t super_page_base = (pointer_as_uint & kSuperPageBaseMask); - return super_page_base + (partition_page_index << PartitionPageShift()); -} - -// Converts an address inside a slot span into a pointer to the SlotSpanMetadata -// object (within super pages's metadata) that describes the slot span -// containing that slot. -// -// CAUTION! For direct-mapped allocation, |address| has to be within the first -// partition page. -template <bool thread_safe> -PA_ALWAYS_INLINE SlotSpanMetadata<thread_safe>* -SlotSpanMetadata<thread_safe>::FromAddr(uintptr_t address) { - auto* page = PartitionPage<thread_safe>::FromAddr(address); - PA_DCHECK(page->is_valid); - // Partition pages in the same slot span share the same SlotSpanMetadata - // object (located in the first PartitionPage object of that span). Adjust - // for that. - page -= page->slot_span_metadata_offset; - PA_DCHECK(page->is_valid); - PA_DCHECK(!page->slot_span_metadata_offset); - auto* slot_span = &page->slot_span_metadata; - // TODO(crbug.com/1257655): See if we can afford to make this a CHECK. - PA_DCHECK(PartitionRoot<thread_safe>::IsValidSlotSpan(slot_span)); - // For direct map, if |address| doesn't point within the first partition page, - // |slot_span_metadata_offset| will be 0, |page| won't get shifted, leaving - // |slot_size| at 0. - PA_DCHECK(slot_span->bucket->slot_size); - return slot_span; -} - -// Like |FromAddr|, but asserts that |slot_start| indeed points to the -// beginning of a slot. It doesn't check if the slot is actually allocated. -// -// This works on direct maps too. -template <bool thread_safe> -PA_ALWAYS_INLINE SlotSpanMetadata<thread_safe>* -SlotSpanMetadata<thread_safe>::FromSlotStart(uintptr_t slot_start) { - auto* slot_span = FromAddr(slot_start); -#if BUILDFLAG(PA_DCHECK_IS_ON) - // Checks that the pointer is a multiple of slot size. - uintptr_t slot_span_start = ToSlotSpanStart(slot_span); - PA_DCHECK(!((slot_start - slot_span_start) % slot_span->bucket->slot_size)); -#endif // BUILDFLAG(PA_DCHECK_IS_ON) - return slot_span; -} - -// Like |FromAddr|, but asserts that |object| indeed points to the beginning of -// an object. It doesn't check if the object is actually allocated. -// -// This works on direct maps too. -template <bool thread_safe> -PA_ALWAYS_INLINE SlotSpanMetadata<thread_safe>* -SlotSpanMetadata<thread_safe>::FromObject(void* object) { - uintptr_t object_addr = ObjectPtr2Addr(object); - auto* slot_span = FromAddr(object_addr); -#if BUILDFLAG(PA_DCHECK_IS_ON) - // Checks that the object is exactly |extras_offset| away from a multiple of - // slot size (i.e. from a slot start). - uintptr_t slot_span_start = ToSlotSpanStart(slot_span); - auto* root = PartitionRoot<thread_safe>::FromSlotSpan(slot_span); - PA_DCHECK((object_addr - slot_span_start) % slot_span->bucket->slot_size == - root->flags.extras_offset); -#endif // BUILDFLAG(PA_DCHECK_IS_ON) - return slot_span; -} - -// Like |FromAddr|, but asserts that |address| indeed points within an object. -// It doesn't check if the object is actually allocated. -// -// CAUTION! For direct-mapped allocation, |address| has to be within the first -// partition page. -template <bool thread_safe> -PA_ALWAYS_INLINE SlotSpanMetadata<thread_safe>* -SlotSpanMetadata<thread_safe>::FromObjectInnerAddr(uintptr_t address) { - auto* slot_span = FromAddr(address); -#if BUILDFLAG(PA_DCHECK_IS_ON) - // Checks that the address is within the expected object boundaries. - uintptr_t slot_span_start = ToSlotSpanStart(slot_span); - auto* root = PartitionRoot<thread_safe>::FromSlotSpan(slot_span); - uintptr_t shift_from_slot_start = - (address - slot_span_start) % slot_span->bucket->slot_size; - PA_DCHECK(shift_from_slot_start >= root->flags.extras_offset); - // Use <= to allow an address immediately past the object. - PA_DCHECK(shift_from_slot_start <= - root->flags.extras_offset + slot_span->GetUsableSize(root)); -#endif // BUILDFLAG(PA_DCHECK_IS_ON) - return slot_span; -} -template <bool thread_safe> -PA_ALWAYS_INLINE SlotSpanMetadata<thread_safe>* -SlotSpanMetadata<thread_safe>::FromObjectInnerPtr(void* ptr) { - return FromObjectInnerAddr(ObjectInnerPtr2Addr(ptr)); -} - -template <bool thread_safe> -PA_ALWAYS_INLINE void SlotSpanMetadata<thread_safe>::SetRawSize( - size_t raw_size) { - PA_DCHECK(CanStoreRawSize()); - auto* subsequent_page_metadata = GetSubsequentPageMetadata( - reinterpret_cast<PartitionPage<thread_safe>*>(this)); - subsequent_page_metadata->raw_size = raw_size; -} - -template <bool thread_safe> -PA_ALWAYS_INLINE size_t SlotSpanMetadata<thread_safe>::GetRawSize() const { - PA_DCHECK(CanStoreRawSize()); - const auto* subsequent_page_metadata = GetSubsequentPageMetadata( - reinterpret_cast<const PartitionPage<thread_safe>*>(this)); - return subsequent_page_metadata->raw_size; -} - -template <bool thread_safe> -PA_ALWAYS_INLINE void SlotSpanMetadata<thread_safe>::SetFreelistHead( - PartitionFreelistEntry* new_head) { -#if BUILDFLAG(PA_DCHECK_IS_ON) - // |this| is in the metadata region, hence isn't MTE-tagged. Untag |new_head| - // as well. - uintptr_t new_head_untagged = UntagPtr(new_head); - PA_DCHECK(!new_head || - (reinterpret_cast<uintptr_t>(this) & kSuperPageBaseMask) == - (new_head_untagged & kSuperPageBaseMask)); -#endif - freelist_head = new_head; - // Inserted something new in the freelist, assume that it is not sorted - // anymore. - freelist_is_sorted_ = false; -} - -template <bool thread_safe> -PA_ALWAYS_INLINE PartitionFreelistEntry* -SlotSpanMetadata<thread_safe>::PopForAlloc(size_t size) { - // Not using bucket->slot_size directly as the compiler doesn't know that - // |bucket->slot_size| is the same as |size|. - PA_DCHECK(size == bucket->slot_size); - PartitionFreelistEntry* result = freelist_head; - // Not setting freelist_is_sorted_ to false since this doesn't destroy - // ordering. - freelist_head = freelist_head->GetNext(size); - num_allocated_slots++; - return result; -} - -template <bool thread_safe> -PA_ALWAYS_INLINE void SlotSpanMetadata<thread_safe>::Free(uintptr_t slot_start) - PA_EXCLUSIVE_LOCKS_REQUIRED( - PartitionRoot<thread_safe>::FromSlotSpan(this)->lock_) { -#if BUILDFLAG(PA_DCHECK_IS_ON) - auto* root = PartitionRoot<thread_safe>::FromSlotSpan(this); - root->lock_.AssertAcquired(); -#endif - - auto* entry = static_cast<internal::PartitionFreelistEntry*>( - SlotStartAddr2Ptr(slot_start)); - // Catches an immediate double free. - PA_CHECK(entry != freelist_head); - // Look for double free one level deeper in debug. - PA_DCHECK(!freelist_head || - entry != freelist_head->GetNext(bucket->slot_size)); - entry->SetNext(freelist_head); - SetFreelistHead(entry); - // A best effort double-free check. Works only on empty slot spans. - PA_CHECK(num_allocated_slots); - --num_allocated_slots; - // If the span is marked full, or became empty, take the slow path to update - // internal state. - if (PA_UNLIKELY(marked_full || num_allocated_slots == 0)) { - FreeSlowPath(1); - } else { - // All single-slot allocations must go through the slow path to - // correctly update the raw size. - PA_DCHECK(!CanStoreRawSize()); - } -} - -template <bool thread_safe> -PA_ALWAYS_INLINE void SlotSpanMetadata<thread_safe>::AppendFreeList( - PartitionFreelistEntry* head, - PartitionFreelistEntry* tail, - size_t number_of_freed) - PA_EXCLUSIVE_LOCKS_REQUIRED( - PartitionRoot<thread_safe>::FromSlotSpan(this)->lock_) { -#if BUILDFLAG(PA_DCHECK_IS_ON) - auto* root = PartitionRoot<thread_safe>::FromSlotSpan(this); - root->lock_.AssertAcquired(); - PA_DCHECK(!tail->GetNext(bucket->slot_size)); - PA_DCHECK(number_of_freed); - PA_DCHECK(num_allocated_slots); - if (CanStoreRawSize()) { - PA_DCHECK(number_of_freed == 1); - } - { - size_t number_of_entries = 0; - for (auto* entry = head; entry; - entry = entry->GetNext(bucket->slot_size), ++number_of_entries) { - uintptr_t untagged_entry = UntagPtr(entry); - // Check that all entries belong to this slot span. - PA_DCHECK(ToSlotSpanStart(this) <= untagged_entry); - PA_DCHECK(untagged_entry < - ToSlotSpanStart(this) + bucket->get_bytes_per_span()); - } - PA_DCHECK(number_of_entries == number_of_freed); - } -#endif - - tail->SetNext(freelist_head); - SetFreelistHead(head); - PA_DCHECK(num_allocated_slots >= number_of_freed); - num_allocated_slots -= number_of_freed; - // If the span is marked full, or became empty, take the slow path to update - // internal state. - if (PA_UNLIKELY(marked_full || num_allocated_slots == 0)) { - FreeSlowPath(number_of_freed); - } else { - // All single-slot allocations must go through the slow path to - // correctly update the raw size. - PA_DCHECK(!CanStoreRawSize()); - } -} - -template <bool thread_safe> -PA_ALWAYS_INLINE bool SlotSpanMetadata<thread_safe>::is_active() const { - PA_DCHECK(this != get_sentinel_slot_span()); - bool ret = - (num_allocated_slots > 0 && (freelist_head || num_unprovisioned_slots)); - if (ret) { - PA_DCHECK(!marked_full); - PA_DCHECK(num_allocated_slots < bucket->get_slots_per_span()); - } - return ret; -} - -template <bool thread_safe> -PA_ALWAYS_INLINE bool SlotSpanMetadata<thread_safe>::is_full() const { - PA_DCHECK(this != get_sentinel_slot_span()); - bool ret = (num_allocated_slots == bucket->get_slots_per_span()); - if (ret) { - PA_DCHECK(!freelist_head); - PA_DCHECK(!num_unprovisioned_slots); - // May or may not be marked full, so don't check for that. - } - return ret; -} - -template <bool thread_safe> -PA_ALWAYS_INLINE bool SlotSpanMetadata<thread_safe>::is_empty() const { - PA_DCHECK(this != get_sentinel_slot_span()); - bool ret = (!num_allocated_slots && freelist_head); - if (ret) { - PA_DCHECK(!marked_full); - } - return ret; -} - -template <bool thread_safe> -PA_ALWAYS_INLINE bool SlotSpanMetadata<thread_safe>::is_decommitted() const { - PA_DCHECK(this != get_sentinel_slot_span()); - bool ret = (!num_allocated_slots && !freelist_head); - if (ret) { - PA_DCHECK(!marked_full); - PA_DCHECK(!num_unprovisioned_slots); - PA_DCHECK(!in_empty_cache_); - } - return ret; -} - -template <bool thread_safe> -PA_ALWAYS_INLINE void SlotSpanMetadata<thread_safe>::Reset() { - PA_DCHECK(is_decommitted()); - - num_unprovisioned_slots = bucket->get_slots_per_span(); - PA_DCHECK(num_unprovisioned_slots); - - ToSuperPageExtent()->IncrementNumberOfNonemptySlotSpans(); - - next_slot_span = nullptr; -} - -#if BUILDFLAG(USE_STARSCAN) -// Returns the state bitmap from an address within a normal-bucket super page. -// It's the caller's responsibility to ensure that the bitmap exists. -PA_ALWAYS_INLINE AllocationStateMap* StateBitmapFromAddr(uintptr_t address) { - PA_DCHECK(IsManagedByNormalBuckets(address)); - uintptr_t super_page = address & kSuperPageBaseMask; - return SuperPageStateBitmap(super_page); -} -#endif // BUILDFLAG(USE_STARSCAN) - -// Iterates over all slot spans in a super-page. |Callback| must return true if -// early return is needed. -template <bool thread_safe, typename Callback> -void IterateSlotSpans(uintptr_t super_page, - bool with_quarantine, - Callback callback) { -#if BUILDFLAG(PA_DCHECK_IS_ON) - PA_DCHECK(!(super_page % kSuperPageAlignment)); - auto* extent_entry = PartitionSuperPageToExtent<thread_safe>(super_page); - extent_entry->root->lock_.AssertAcquired(); -#endif - - using Page = PartitionPage<thread_safe>; - using SlotSpan = SlotSpanMetadata<thread_safe>; - auto* const first_page = - Page::FromAddr(SuperPagePayloadBegin(super_page, with_quarantine)); - auto* const last_page = - Page::FromAddr(SuperPagePayloadEnd(super_page) - PartitionPageSize()); - Page* page; - SlotSpan* slot_span; - for (page = first_page; page <= last_page;) { - PA_DCHECK(!page->slot_span_metadata_offset); // Ensure slot span beginning. - if (!page->is_valid) { - if (page->has_valid_span_after_this) { - // The page doesn't represent a valid slot span, but there is another - // one somewhere after this. Keep iterating to find it. - ++page; - continue; - } - // There are currently no valid spans from here on. No need to iterate - // the rest of the super page. - break; - } - slot_span = &page->slot_span_metadata; - if (callback(slot_span)) { - return; - } - page += slot_span->bucket->get_pages_per_slot_span(); - } - // Each super page must have at least one valid slot span. - PA_DCHECK(page > first_page); - // Just a quick check that the search ended at a valid slot span and there - // was no unnecessary iteration over gaps afterwards. - PA_DCHECK(page == reinterpret_cast<Page*>(slot_span) + - slot_span->bucket->get_pages_per_slot_span()); -} - -} // namespace partition_alloc::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_PAGE_H_
diff --git a/base/allocator/partition_allocator/partition_ref_count.h b/base/allocator/partition_allocator/partition_ref_count.h deleted file mode 100644 index ead781f..0000000 --- a/base/allocator/partition_allocator/partition_ref_count.h +++ /dev/null
@@ -1,484 +0,0 @@ -// 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 BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_REF_COUNT_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_REF_COUNT_H_ - -#include <atomic> -#include <cstdint> - -#include "base/allocator/partition_allocator/dangling_raw_ptr_checks.h" -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/partition_alloc_config.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "base/allocator/partition_allocator/partition_alloc_forward.h" -#include "base/allocator/partition_allocator/tagging.h" -#include "build/build_config.h" - -namespace partition_alloc::internal { - -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - -// Special-purpose atomic reference count class used by RawPtrBackupRefImpl. -// The least significant bit of the count is reserved for tracking the liveness -// state of an allocation: it's set when the allocation is created and cleared -// on free(). So the count can be: -// -// 1 for an allocation that is just returned from Alloc() -// 2 * k + 1 for a "live" allocation with k references -// 2 * k for an allocation with k dangling references after Free() -// -// This protects against double-free's, as we check whether the reference count -// is odd in |ReleaseFromAllocator()|, and if not we have a double-free. -class PA_COMPONENT_EXPORT(PARTITION_ALLOC) PartitionRefCount { - public: - // This class holds an atomic bit field: `count_`. It holds up to 5 values: - // - // bits name description - // ----- --------------------- ---------------------------------------- - // 0 is_allocated Whether or not the memory is held by the - // allocator. - // - 1 at construction time. - // - Decreased in ReleaseFromAllocator(); - // - // 1-31 ptr_count Number of raw_ptr<T>. - // - Increased in Acquire() - // - Decreased in Release() - // - // 32 dangling_detected A dangling raw_ptr<> has been detected. - // 33 needs_mac11_malloc_ Whether malloc_size() return value needs to - // size_hack be adjusted for this allocation. - // - // 34-63 unprotected_ptr_count Number of - // raw_ptr<T, DisableDanglingPtrDetection> - // - Increased in AcquireFromUnprotectedPtr(). - // - Decreased in ReleaseFromUnprotectedPtr(). - // - // The allocation is reclaimed if all of: - // - |is_allocated| - // - |ptr_count| - // - |unprotected_ptr_count| - // are zero. - // - // During ReleaseFromAllocator(), if |ptr_count| is not zero, - // |dangling_detected| is set and the error is reported via - // DanglingRawPtrDetected(id). The matching DanglingRawPtrReleased(id) will be - // called when the last raw_ptr<> is released. -#if BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) - using CountType = uint64_t; - static constexpr CountType kMemoryHeldByAllocatorBit = 0x0000'0000'0000'0001; - static constexpr CountType kPtrCountMask = 0x0000'0000'FFFF'FFFE; - static constexpr CountType kUnprotectedPtrCountMask = 0xFFFF'FFFC'0000'0000; - static constexpr CountType kDanglingRawPtrDetectedBit = 0x0000'0001'0000'0000; - static constexpr CountType kNeedsMac11MallocSizeHackBit = - 0x0000'0002'0000'0000; - - static constexpr CountType kPtrInc = 0x0000'0000'0000'0002; - static constexpr CountType kUnprotectedPtrInc = 0x0000'0004'0000'0000; -#else - using CountType = uint32_t; - static constexpr CountType kMemoryHeldByAllocatorBit = 0x0000'0001; - - static constexpr CountType kPtrCountMask = 0x7FFF'FFFE; - static constexpr CountType kUnprotectedPtrCountMask = 0x0000'0000; - static constexpr CountType kDanglingRawPtrDetectedBit = 0x0000'0000; - static constexpr CountType kNeedsMac11MallocSizeHackBit = 0x8000'0000; - - static constexpr CountType kPtrInc = 0x0000'0002; -#endif - - PA_ALWAYS_INLINE explicit PartitionRefCount( - bool needs_mac11_malloc_size_hack); - - // Incrementing the counter doesn't imply any visibility about modified - // memory, hence relaxed atomics. For decrement, visibility is required before - // the memory gets freed, necessitating an acquire/release barrier before - // freeing the memory. - // - // For details, see base::AtomicRefCount, which has the same constraints and - // characteristics. - // - // FYI: The assembly produced by the compiler on every platform, in particular - // the uint64_t fetch_add on 32bit CPU. - // https://docs.google.com/document/d/1cSTVDVEE-8l2dXLPcfyN75r6ihMbeiSp1ncL9ae3RZE - PA_ALWAYS_INLINE void Acquire() { - CheckCookieIfSupported(); - -#if BUILDFLAG(ENABLE_DANGLING_RAW_PTR_PERF_EXPERIMENT) - constexpr CountType kInc = kUnprotectedPtrInc; - constexpr CountType kMask = kUnprotectedPtrCountMask; -#else - constexpr CountType kInc = kPtrInc; - constexpr CountType kMask = kPtrCountMask; -#endif - CountType old_count = count_.fetch_add(kInc, std::memory_order_relaxed); - // Check overflow. - PA_CHECK((old_count & kMask) != kMask); - } - - // Similar to |Acquire()|, but for raw_ptr<T, DisableDanglingPtrDetection> - // instead of raw_ptr<T>. - PA_ALWAYS_INLINE void AcquireFromUnprotectedPtr() { -#if BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) - CheckCookieIfSupported(); - CountType old_count = - count_.fetch_add(kUnprotectedPtrInc, std::memory_order_relaxed); - // Check overflow. - PA_CHECK((old_count & kUnprotectedPtrCountMask) != - kUnprotectedPtrCountMask); -#else - Acquire(); -#endif - } - - // Returns true if the allocation should be reclaimed. - PA_ALWAYS_INLINE bool Release() { -#if BUILDFLAG(ENABLE_DANGLING_RAW_PTR_PERF_EXPERIMENT) - constexpr CountType kInc = kUnprotectedPtrInc; - constexpr CountType kMask = kUnprotectedPtrCountMask; -#else - constexpr CountType kInc = kPtrInc; - constexpr CountType kMask = kPtrCountMask; -#endif - CheckCookieIfSupported(); - - CountType old_count = count_.fetch_sub(kInc, std::memory_order_release); - // Check underflow. - PA_DCHECK(old_count & kMask); - -#if BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) - // If a dangling raw_ptr<> was detected, report it. - if (PA_UNLIKELY((old_count & kDanglingRawPtrDetectedBit) == - kDanglingRawPtrDetectedBit)) { - partition_alloc::internal::DanglingRawPtrReleased( - reinterpret_cast<uintptr_t>(this)); - } -#endif - - return ReleaseCommon(old_count - kInc); - } - - // Similar to |Release()|, but for raw_ptr<T, DisableDanglingPtrDetection> - // instead of raw_ptr<T>. - PA_ALWAYS_INLINE bool ReleaseFromUnprotectedPtr() { -#if BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) - CheckCookieIfSupported(); - - CountType old_count = - count_.fetch_sub(kUnprotectedPtrInc, std::memory_order_release); - // Check underflow. - PA_DCHECK(old_count & kUnprotectedPtrCountMask); - - return ReleaseCommon(old_count - kUnprotectedPtrInc); -#else - return Release(); -#endif - } - - // Returns true if the allocation should be reclaimed. - // This function should be called by the allocator during Free(). - PA_ALWAYS_INLINE bool ReleaseFromAllocator() { - CheckCookieIfSupported(); - - // TODO(bartekn): Make the double-free check more effective. Once freed, the - // ref-count is overwritten by an encoded freelist-next pointer. - CountType old_count = - count_.fetch_and(~kMemoryHeldByAllocatorBit, std::memory_order_release); - - if (PA_UNLIKELY(!(old_count & kMemoryHeldByAllocatorBit))) { - DoubleFreeOrCorruptionDetected(old_count); - } - - if (PA_LIKELY((old_count & ~kNeedsMac11MallocSizeHackBit) == - kMemoryHeldByAllocatorBit)) { - std::atomic_thread_fence(std::memory_order_acquire); - // The allocation is about to get freed, so clear the cookie. - ClearCookieIfSupported(); - return true; - } - -#if BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) - // Check if any raw_ptr<> still exists. It is now dangling. - if (PA_UNLIKELY(old_count & kPtrCountMask)) { - count_.fetch_or(kDanglingRawPtrDetectedBit, std::memory_order_relaxed); - partition_alloc::internal::DanglingRawPtrDetected( - reinterpret_cast<uintptr_t>(this)); - } -#endif // BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) - return false; - } - - // "IsAlive" means is allocated and not freed. "KnownRefs" refers to - // raw_ptr<T> references. There may be other references from raw pointers or - // unique_ptr, but we have no way of tracking them, so we hope for the best. - // To summarize, the function returns whether we believe the allocation can be - // safely freed. - PA_ALWAYS_INLINE bool IsAliveWithNoKnownRefs() { - CheckCookieIfSupported(); - return (count_.load(std::memory_order_acquire) & - ~kNeedsMac11MallocSizeHackBit) == kMemoryHeldByAllocatorBit; - } - - PA_ALWAYS_INLINE bool IsAlive() { - bool alive = - count_.load(std::memory_order_relaxed) & kMemoryHeldByAllocatorBit; - if (alive) { - CheckCookieIfSupported(); - } - return alive; - } - - // Called when a raw_ptr is not banning dangling ptrs, but the user still - // wants to ensure the pointer is not currently dangling. This is currently - // used in UnretainedWrapper to make sure callbacks are not invoked with - // dangling pointers. If such a raw_ptr exists but the allocation is no longer - // alive, then we have a dangling pointer to a dead object. - PA_ALWAYS_INLINE void ReportIfDangling() { - if (!IsAlive()) { - partition_alloc::internal::UnretainedDanglingRawPtrDetected( - reinterpret_cast<uintptr_t>(this)); - } - } - - // GWP-ASan slots are assigned an extra reference (note `kPtrInc` below) to - // make sure the `raw_ptr<T>` release operation will never attempt to call the - // PA `free` on such a slot. GWP-ASan takes the extra reference into account - // when determining whether the slot can be reused. - PA_ALWAYS_INLINE void InitalizeForGwpAsan() { -#if PA_CONFIG(REF_COUNT_CHECK_COOKIE) - brp_cookie_ = CalculateCookie(); -#endif - count_.store(kPtrInc | kMemoryHeldByAllocatorBit, - std::memory_order_release); - } - - PA_ALWAYS_INLINE bool CanBeReusedByGwpAsan() { - return (count_.load(std::memory_order_acquire) & - ~kNeedsMac11MallocSizeHackBit) == - (kPtrInc | kMemoryHeldByAllocatorBit); - } - - bool NeedsMac11MallocSizeHack() { - return count_.load(std::memory_order_relaxed) & - kNeedsMac11MallocSizeHackBit; - } - -#if PA_CONFIG(REF_COUNT_STORE_REQUESTED_SIZE) - PA_ALWAYS_INLINE void SetRequestedSize(size_t size) { - requested_size_ = static_cast<uint32_t>(size); - } - PA_ALWAYS_INLINE uint32_t requested_size() const { return requested_size_; } -#endif // PA_CONFIG(REF_COUNT_STORE_REQUESTED_SIZE) - - private: - // The common parts shared by Release() and ReleaseFromUnprotectedPtr(). - // Called after updating the ref counts, |count| is the new value of |count_| - // set by fetch_sub. Returns true if memory can be reclaimed. - PA_ALWAYS_INLINE bool ReleaseCommon(CountType count) { - // Do not release memory, if it is still held by any of: - // - The allocator - // - A raw_ptr<T> - // - A raw_ptr<T, DisableDanglingPtrDetection> - // - // Assuming this raw_ptr is not dangling, the memory must still be held at - // least by the allocator, so this is PA_LIKELY true. - if (PA_LIKELY((count & (kMemoryHeldByAllocatorBit | kPtrCountMask | - kUnprotectedPtrCountMask)))) { - return false; // Do not release the memory. - } - - // In most thread-safe reference count implementations, an acquire - // barrier is required so that all changes made to an object from other - // threads are visible to its destructor. In our case, the destructor - // finishes before the final `Release` call, so it shouldn't be a problem. - // However, we will keep it as a precautionary measure. - std::atomic_thread_fence(std::memory_order_acquire); - - // The allocation is about to get freed, so clear the cookie. - ClearCookieIfSupported(); - return true; - } - - // The cookie helps us ensure that: - // 1) The reference count pointer calculation is correct. - // 2) The returned allocation slot is not freed. - PA_ALWAYS_INLINE void CheckCookieIfSupported() { -#if PA_CONFIG(REF_COUNT_CHECK_COOKIE) - PA_CHECK(brp_cookie_ == CalculateCookie()); -#endif - } - - PA_ALWAYS_INLINE void ClearCookieIfSupported() { -#if PA_CONFIG(REF_COUNT_CHECK_COOKIE) - brp_cookie_ = 0; -#endif - } - -#if PA_CONFIG(REF_COUNT_CHECK_COOKIE) - PA_ALWAYS_INLINE uint32_t CalculateCookie() { - return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(this)) ^ - kCookieSalt; - } -#endif // PA_CONFIG(REF_COUNT_CHECK_COOKIE) - - [[noreturn]] PA_NOINLINE PA_NOT_TAIL_CALLED void - DoubleFreeOrCorruptionDetected(CountType count) { - PA_DEBUG_DATA_ON_STACK("refcount", count); - PA_NO_CODE_FOLDING(); - PA_IMMEDIATE_CRASH(); - } - - // Note that in free slots, this is overwritten by encoded freelist - // pointer(s). The way the pointers are encoded on 64-bit little-endian - // architectures, count_ happens stay even, which works well with the - // double-free-detection in ReleaseFromAllocator(). Don't change the layout of - // this class, to preserve this functionality. - std::atomic<CountType> count_; - -#if PA_CONFIG(REF_COUNT_CHECK_COOKIE) - static constexpr uint32_t kCookieSalt = 0xc01dbeef; - volatile uint32_t brp_cookie_; -#endif - -#if PA_CONFIG(REF_COUNT_STORE_REQUESTED_SIZE) - uint32_t requested_size_; -#endif -}; - -PA_ALWAYS_INLINE PartitionRefCount::PartitionRefCount( - bool needs_mac11_malloc_size_hack) - : count_(kMemoryHeldByAllocatorBit | - (needs_mac11_malloc_size_hack ? kNeedsMac11MallocSizeHackBit : 0)) -#if PA_CONFIG(REF_COUNT_CHECK_COOKIE) - , - brp_cookie_(CalculateCookie()) -#endif -{ -} - -#if BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) - -static_assert(kAlignment % alignof(PartitionRefCount) == 0, - "kAlignment must be multiples of alignof(PartitionRefCount)."); - -// Allocate extra space for the reference count to satisfy the alignment -// requirement. -static constexpr size_t kInSlotRefCountBufferSize = sizeof(PartitionRefCount); -constexpr size_t kPartitionRefCountOffsetAdjustment = 0; -constexpr size_t kPartitionPastAllocationAdjustment = 0; - -#if BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) - -#if PA_CONFIG(REF_COUNT_CHECK_COOKIE) || \ - PA_CONFIG(REF_COUNT_STORE_REQUESTED_SIZE) -static constexpr size_t kPartitionRefCountSizeShift = 4; -#else -static constexpr size_t kPartitionRefCountSizeShift = 3; -#endif - -#else // BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) - -#if PA_CONFIG(REF_COUNT_CHECK_COOKIE) && \ - PA_CONFIG(REF_COUNT_STORE_REQUESTED_SIZE) -static constexpr size_t kPartitionRefCountSizeShift = 4; -#elif PA_CONFIG(REF_COUNT_CHECK_COOKIE) || \ - PA_CONFIG(REF_COUNT_STORE_REQUESTED_SIZE) -static constexpr size_t kPartitionRefCountSizeShift = 3; -#else -static constexpr size_t kPartitionRefCountSizeShift = 2; -#endif - -#endif // PA_CONFIG(REF_COUNT_CHECK_COOKIE) -static_assert((1 << kPartitionRefCountSizeShift) == sizeof(PartitionRefCount)); - -// We need one PartitionRefCount for each system page in a super page. They take -// `x = sizeof(PartitionRefCount) * (kSuperPageSize / SystemPageSize())` space. -// They need to fit into a system page of metadata as sparsely as possible to -// minimize cache line sharing, hence we calculate a multiplier as -// `SystemPageSize() / x`. -// -// The multiplier is expressed as a bitshift to optimize the code generation. -// SystemPageSize() isn't always a constrexpr, in which case the compiler -// wouldn't know it's a power of two. The equivalence of these calculations is -// checked in PartitionAllocGlobalInit(). -PA_ALWAYS_INLINE static PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t -GetPartitionRefCountIndexMultiplierShift() { - return SystemPageShift() * 2 - kSuperPageShift - kPartitionRefCountSizeShift; -} - -PA_ALWAYS_INLINE PartitionRefCount* PartitionRefCountPointer( - uintptr_t slot_start) { -#if BUILDFLAG(PA_DCHECK_IS_ON) || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) - CheckThatSlotOffsetIsZero(slot_start); -#endif - if (PA_LIKELY(slot_start & SystemPageOffsetMask())) { - uintptr_t refcount_address = slot_start - sizeof(PartitionRefCount); -#if BUILDFLAG(PA_DCHECK_IS_ON) || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) - PA_CHECK(refcount_address % alignof(PartitionRefCount) == 0); -#endif - // Have to MTE-tag, because the address is untagged, but lies within a slot - // area, which is protected by MTE. - // - // There could be a race condition though if the previous slot is - // freed/retagged concurrently, so ideally the ref count should occupy its - // own MTE granule. - // TODO(richard.townsend@arm.com): improve this. - return static_cast<PartitionRefCount*>(TagAddr(refcount_address)); - } else { - // No need to tag, as the metadata region isn't protected by MTE. - PartitionRefCount* bitmap_base = reinterpret_cast<PartitionRefCount*>( - (slot_start & kSuperPageBaseMask) + SystemPageSize() * 2); - size_t index = ((slot_start & kSuperPageOffsetMask) >> SystemPageShift()) - << GetPartitionRefCountIndexMultiplierShift(); -#if BUILDFLAG(PA_DCHECK_IS_ON) || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) - PA_CHECK(sizeof(PartitionRefCount) * index <= SystemPageSize()); -#endif - return bitmap_base + index; - } -} - -#else // BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) - -// Allocate extra space for the reference count to satisfy the alignment -// requirement. -static constexpr size_t kInSlotRefCountBufferSize = kAlignment; -constexpr size_t kPartitionRefCountOffsetAdjustment = kInSlotRefCountBufferSize; - -// This is for adjustment of pointers right past the allocation, which may point -// to the next slot. First subtract 1 to bring them to the intended slot, and -// only then we'll be able to find ref-count in that slot. -constexpr size_t kPartitionPastAllocationAdjustment = 1; - -PA_ALWAYS_INLINE PartitionRefCount* PartitionRefCountPointer( - uintptr_t slot_start) { -#if BUILDFLAG(PA_DCHECK_IS_ON) || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) - CheckThatSlotOffsetIsZero(slot_start); -#endif - // Have to MTE-tag, because the address is untagged, but lies within a slot - // area, which is protected by MTE. - return static_cast<PartitionRefCount*>(TagAddr(slot_start)); -} - -#endif // BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) - -static_assert(sizeof(PartitionRefCount) <= kInSlotRefCountBufferSize, - "PartitionRefCount should fit into the in-slot buffer."); - -#else // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - -static constexpr size_t kInSlotRefCountBufferSize = 0; -constexpr size_t kPartitionRefCountOffsetAdjustment = 0; - -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - -constexpr size_t kPartitionRefCountSizeAdjustment = kInSlotRefCountBufferSize; - -} // namespace partition_alloc::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_REF_COUNT_H_
diff --git a/base/allocator/partition_allocator/partition_root.cc b/base/allocator/partition_allocator/partition_root.cc deleted file mode 100644 index ff2d108..0000000 --- a/base/allocator/partition_allocator/partition_root.cc +++ /dev/null
@@ -1,1677 +0,0 @@ -// 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. - -#include "base/allocator/partition_allocator/partition_root.h" - -#include <cstdint> - -#include "base/allocator/partition_allocator/freeslot_bitmap.h" -#include "base/allocator/partition_allocator/oom.h" -#include "base/allocator/partition_allocator/page_allocator.h" -#include "base/allocator/partition_allocator/partition_address_space.h" -#include "base/allocator/partition_allocator/partition_alloc_base/bits.h" -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_base/thread_annotations.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/partition_alloc_config.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "base/allocator/partition_allocator/partition_bucket.h" -#include "base/allocator/partition_allocator/partition_cookie.h" -#include "base/allocator/partition_allocator/partition_oom.h" -#include "base/allocator/partition_allocator/partition_page.h" -#include "base/allocator/partition_allocator/partition_ref_count.h" -#include "base/allocator/partition_allocator/pkey.h" -#include "base/allocator/partition_allocator/reservation_offset_table.h" -#include "base/allocator/partition_allocator/tagging.h" -#include "build/build_config.h" - -#if PA_CONFIG(ENABLE_MAC11_MALLOC_SIZE_HACK) && BUILDFLAG(IS_APPLE) -#include "base/allocator/partition_allocator/partition_alloc_base/mac/mac_util.h" -#endif // PA_CONFIG(ENABLE_MAC11_MALLOC_SIZE_HACK) && BUILDFLAG(IS_APPLE) - -#if BUILDFLAG(USE_STARSCAN) -#include "base/allocator/partition_allocator/starscan/pcscan.h" -#endif - -#if !BUILDFLAG(HAS_64_BIT_POINTERS) -#include "base/allocator/partition_allocator/address_pool_manager_bitmap.h" -#endif - -#if BUILDFLAG(IS_WIN) -#include <windows.h> -#include "wow64apiset.h" -#endif - -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) -#include <pthread.h> -#endif - -namespace partition_alloc::internal { - -#if BUILDFLAG(RECORD_ALLOC_INFO) -// Even if this is not hidden behind a BUILDFLAG, it should not use any memory -// when recording is disabled, since it ends up in the .bss section. -AllocInfo g_allocs = {}; - -void RecordAllocOrFree(uintptr_t addr, size_t size) { - g_allocs.allocs[g_allocs.index.fetch_add(1, std::memory_order_relaxed) % - kAllocInfoSize] = {addr, size}; -} -#endif // BUILDFLAG(RECORD_ALLOC_INFO) - -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) -PtrPosWithinAlloc IsPtrWithinSameAlloc(uintptr_t orig_address, - uintptr_t test_address, - size_t type_size) { - // Required for pointers right past an allocation. See - // |PartitionAllocGetSlotStartInBRPPool()|. - uintptr_t adjusted_address = - orig_address - kPartitionPastAllocationAdjustment; - PA_DCHECK(IsManagedByNormalBucketsOrDirectMap(adjusted_address)); - DCheckIfManagedByPartitionAllocBRPPool(adjusted_address); - - uintptr_t slot_start = PartitionAllocGetSlotStartInBRPPool(adjusted_address); - // Don't use |adjusted_address| beyond this point at all. It was needed to - // pick the right slot, but now we're dealing with very concrete addresses. - // Zero it just in case, to catch errors. - adjusted_address = 0; - - auto* slot_span = SlotSpanMetadata<ThreadSafe>::FromSlotStart(slot_start); - auto* root = PartitionRoot<ThreadSafe>::FromSlotSpan(slot_span); - // Double check that ref-count is indeed present. - PA_DCHECK(root->brp_enabled()); - - uintptr_t object_addr = root->SlotStartToObjectAddr(slot_start); - uintptr_t object_end = object_addr + slot_span->GetUsableSize(root); - if (test_address < object_addr || object_end < test_address) { - return PtrPosWithinAlloc::kFarOOB; -#if BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR) - } else if (object_end - type_size < test_address) { - // Not even a single element of the type referenced by the pointer can fit - // between the pointer and the end of the object. - return PtrPosWithinAlloc::kAllocEnd; -#endif - } else { - return PtrPosWithinAlloc::kInBounds; - } -} -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - -} // namespace partition_alloc::internal - -namespace partition_alloc { - -#if PA_CONFIG(USE_PARTITION_ROOT_ENUMERATOR) - -namespace { - -internal::Lock g_root_enumerator_lock; -} - -template <bool thread_safe> -internal::Lock& PartitionRoot<thread_safe>::GetEnumeratorLock() { - return g_root_enumerator_lock; -} - -namespace internal { - -class PartitionRootEnumerator { - public: - using EnumerateCallback = void (*)(ThreadSafePartitionRoot* root, - bool in_child); - enum EnumerateOrder { - kNormal, - kReverse, - }; - - static PartitionRootEnumerator& Instance() { - static PartitionRootEnumerator instance; - return instance; - } - - void Enumerate(EnumerateCallback callback, - bool in_child, - EnumerateOrder order) PA_NO_THREAD_SAFETY_ANALYSIS { - if (order == kNormal) { - ThreadSafePartitionRoot* root; - for (root = Head(partition_roots_); root != nullptr; - root = root->next_root) { - callback(root, in_child); - } - } else { - PA_DCHECK(order == kReverse); - ThreadSafePartitionRoot* root; - for (root = Tail(partition_roots_); root != nullptr; - root = root->prev_root) { - callback(root, in_child); - } - } - } - - void Register(ThreadSafePartitionRoot* root) { - internal::ScopedGuard guard(ThreadSafePartitionRoot::GetEnumeratorLock()); - root->next_root = partition_roots_; - root->prev_root = nullptr; - if (partition_roots_) { - partition_roots_->prev_root = root; - } - partition_roots_ = root; - } - - void Unregister(ThreadSafePartitionRoot* root) { - internal::ScopedGuard guard(ThreadSafePartitionRoot::GetEnumeratorLock()); - ThreadSafePartitionRoot* prev = root->prev_root; - ThreadSafePartitionRoot* next = root->next_root; - if (prev) { - PA_DCHECK(prev->next_root == root); - prev->next_root = next; - } else { - PA_DCHECK(partition_roots_ == root); - partition_roots_ = next; - } - if (next) { - PA_DCHECK(next->prev_root == root); - next->prev_root = prev; - } - root->next_root = nullptr; - root->prev_root = nullptr; - } - - private: - constexpr PartitionRootEnumerator() = default; - - ThreadSafePartitionRoot* Head(ThreadSafePartitionRoot* roots) { - return roots; - } - - ThreadSafePartitionRoot* Tail(ThreadSafePartitionRoot* roots) - PA_NO_THREAD_SAFETY_ANALYSIS { - if (!roots) { - return nullptr; - } - ThreadSafePartitionRoot* node = roots; - for (; node->next_root != nullptr; node = node->next_root) - ; - return node; - } - - ThreadSafePartitionRoot* partition_roots_ - PA_GUARDED_BY(ThreadSafePartitionRoot::GetEnumeratorLock()) = nullptr; -}; - -} // namespace internal - -#endif // PA_USE_PARTITION_ROOT_ENUMERATOR - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - -namespace { - -#if PA_CONFIG(HAS_ATFORK_HANDLER) - -void LockRoot(PartitionRoot<internal::ThreadSafe>* root, - bool) PA_NO_THREAD_SAFETY_ANALYSIS { - PA_DCHECK(root); - root->lock_.Acquire(); -} - -// PA_NO_THREAD_SAFETY_ANALYSIS: acquires the lock and doesn't release it, by -// design. -void BeforeForkInParent() PA_NO_THREAD_SAFETY_ANALYSIS { - // ThreadSafePartitionRoot::GetLock() is private. So use - // g_root_enumerator_lock here. - g_root_enumerator_lock.Acquire(); - internal::PartitionRootEnumerator::Instance().Enumerate( - LockRoot, false, - internal::PartitionRootEnumerator::EnumerateOrder::kNormal); - - ThreadCacheRegistry::GetLock().Acquire(); -} - -template <typename T> -void UnlockOrReinit(T& lock, bool in_child) PA_NO_THREAD_SAFETY_ANALYSIS { - // Only re-init the locks in the child process, in the parent can unlock - // normally. - if (in_child) { - lock.Reinit(); - } else { - lock.Release(); - } -} - -void UnlockOrReinitRoot(PartitionRoot<internal::ThreadSafe>* root, - bool in_child) PA_NO_THREAD_SAFETY_ANALYSIS { - UnlockOrReinit(root->lock_, in_child); -} - -void ReleaseLocks(bool in_child) PA_NO_THREAD_SAFETY_ANALYSIS { - // In reverse order, even though there are no lock ordering dependencies. - UnlockOrReinit(ThreadCacheRegistry::GetLock(), in_child); - internal::PartitionRootEnumerator::Instance().Enumerate( - UnlockOrReinitRoot, in_child, - internal::PartitionRootEnumerator::EnumerateOrder::kReverse); - - // ThreadSafePartitionRoot::GetLock() is private. So use - // g_root_enumerator_lock here. - UnlockOrReinit(g_root_enumerator_lock, in_child); -} - -void AfterForkInParent() { - ReleaseLocks(/* in_child = */ false); -} - -void AfterForkInChild() { - ReleaseLocks(/* in_child = */ true); - // Unsafe, as noted in the name. This is fine here however, since at this - // point there is only one thread, this one (unless another post-fork() - // handler created a thread, but it would have needed to allocate, which would - // have deadlocked the process already). - // - // If we don't reclaim this memory, it is lost forever. Note that this is only - // really an issue if we fork() a multi-threaded process without calling - // exec() right away, which is discouraged. - ThreadCacheRegistry::Instance().ForcePurgeAllThreadAfterForkUnsafe(); -} -#endif // PA_CONFIG(HAS_ATFORK_HANDLER) - -std::atomic<bool> g_global_init_called; -void PartitionAllocMallocInitOnce() { - bool expected = false; - // No need to block execution for potential concurrent initialization, merely - // want to make sure this is only called once. - if (!g_global_init_called.compare_exchange_strong(expected, true)) { - return; - } - -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) - // When fork() is called, only the current thread continues to execute in the - // child process. If the lock is held, but *not* by this thread when fork() is - // called, we have a deadlock. - // - // The "solution" here is to acquire the lock on the forking thread before - // fork(), and keep it held until fork() is done, in the parent and the - // child. To clean up memory, we also must empty the thread caches in the - // child, which is easier, since no threads except for the current one are - // running right after the fork(). - // - // This is not perfect though, since: - // - Multiple pre/post-fork() handlers can be registered, they are then run in - // LIFO order for the pre-fork handler, and FIFO order for the post-fork - // one. So unless we are the first to register a handler, if another handler - // allocates, then we deterministically deadlock. - // - pthread handlers are *not* called when the application calls clone() - // directly, which is what Chrome does to launch processes. - // - // However, no perfect solution really exists to make threads + fork() - // cooperate, but deadlocks are real (and fork() is used in DEATH_TEST()s), - // and other malloc() implementations use the same techniques. - int err = - pthread_atfork(BeforeForkInParent, AfterForkInParent, AfterForkInChild); - PA_CHECK(err == 0); -#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) -} - -} // namespace - -#if BUILDFLAG(IS_APPLE) -void PartitionAllocMallocHookOnBeforeForkInParent() { - BeforeForkInParent(); -} - -void PartitionAllocMallocHookOnAfterForkInParent() { - AfterForkInParent(); -} - -void PartitionAllocMallocHookOnAfterForkInChild() { - AfterForkInChild(); -} -#endif // BUILDFLAG(IS_APPLE) - -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - -namespace internal { - -namespace { -// 64 was chosen arbitrarily, as it seems like a reasonable trade-off between -// performance and purging opportunity. Higher value (i.e. smaller slots) -// wouldn't necessarily increase chances of purging, but would result in -// more work and larger |slot_usage| array. Lower value would probably decrease -// chances of purging. Not empirically tested. -constexpr size_t kMaxPurgeableSlotsPerSystemPage = 64; -PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t -MinPurgeableSlotSize() { - return SystemPageSize() / kMaxPurgeableSlotsPerSystemPage; -} -} // namespace - -template <bool thread_safe> -static size_t PartitionPurgeSlotSpan( - internal::SlotSpanMetadata<thread_safe>* slot_span, - bool discard) { - auto* root = PartitionRoot<thread_safe>::FromSlotSpan(slot_span); - const internal::PartitionBucket<thread_safe>* bucket = slot_span->bucket; - size_t slot_size = bucket->slot_size; - - if (slot_size < MinPurgeableSlotSize() || !slot_span->num_allocated_slots) { - return 0; - } - - size_t bucket_num_slots = bucket->get_slots_per_span(); - size_t discardable_bytes = 0; - - if (slot_span->CanStoreRawSize()) { - uint32_t utilized_slot_size = static_cast<uint32_t>( - RoundUpToSystemPage(slot_span->GetUtilizedSlotSize())); - discardable_bytes = bucket->slot_size - utilized_slot_size; - if (discardable_bytes && discard) { - uintptr_t slot_span_start = - internal::SlotSpanMetadata<thread_safe>::ToSlotSpanStart(slot_span); - uintptr_t committed_data_end = slot_span_start + utilized_slot_size; - ScopedSyscallTimer timer{root}; - DiscardSystemPages(committed_data_end, discardable_bytes); - } - return discardable_bytes; - } - -#if defined(PAGE_ALLOCATOR_CONSTANTS_ARE_CONSTEXPR) - constexpr size_t kMaxSlotCount = - (PartitionPageSize() * kMaxPartitionPagesPerRegularSlotSpan) / - MinPurgeableSlotSize(); -#elif BUILDFLAG(IS_APPLE) || (BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64)) - // It's better for slot_usage to be stack-allocated and fixed-size, which - // demands that its size be constexpr. On IS_APPLE and Linux on arm64, - // PartitionPageSize() is always SystemPageSize() << 2, so regardless of - // what the run time page size is, kMaxSlotCount can always be simplified - // to this expression. - constexpr size_t kMaxSlotCount = - 4 * kMaxPurgeableSlotsPerSystemPage * - internal::kMaxPartitionPagesPerRegularSlotSpan; - PA_CHECK(kMaxSlotCount == (PartitionPageSize() * - internal::kMaxPartitionPagesPerRegularSlotSpan) / - MinPurgeableSlotSize()); -#endif - PA_DCHECK(bucket_num_slots <= kMaxSlotCount); - PA_DCHECK(slot_span->num_unprovisioned_slots < bucket_num_slots); - size_t num_slots = bucket_num_slots - slot_span->num_unprovisioned_slots; - char slot_usage[kMaxSlotCount]; -#if !BUILDFLAG(IS_WIN) - // The last freelist entry should not be discarded when using OS_WIN. - // DiscardVirtualMemory makes the contents of discarded memory undefined. - size_t last_slot = static_cast<size_t>(-1); -#endif - memset(slot_usage, 1, num_slots); - uintptr_t slot_span_start = - SlotSpanMetadata<thread_safe>::ToSlotSpanStart(slot_span); - // First, walk the freelist for this slot span and make a bitmap of which - // slots are not in use. - for (PartitionFreelistEntry* entry = slot_span->get_freelist_head(); entry; - /**/) { - size_t slot_number = - bucket->GetSlotNumber(SlotStartPtr2Addr(entry) - slot_span_start); - PA_DCHECK(slot_number < num_slots); - slot_usage[slot_number] = 0; -#if !BUILDFLAG(IS_WIN) - // If we have a slot where the encoded next pointer is 0, we can actually - // discard that entry because touching a discarded page is guaranteed to - // return the original content or 0. (Note that this optimization won't be - // effective on big-endian machines because the masking function is - // negation.) - if (entry->IsEncodedNextPtrZero()) { - last_slot = slot_number; - } -#endif - entry = entry->GetNext(slot_size); - } - - // If the slot(s) at the end of the slot span are not in used, we can truncate - // them entirely and rewrite the freelist. - size_t truncated_slots = 0; - while (!slot_usage[num_slots - 1]) { - truncated_slots++; - num_slots--; - PA_DCHECK(num_slots); - } - // First, do the work of calculating the discardable bytes. Don't actually - // discard anything unless the discard flag was passed in. - if (truncated_slots) { - size_t unprovisioned_bytes = 0; - uintptr_t begin_addr = slot_span_start + (num_slots * slot_size); - uintptr_t end_addr = begin_addr + (slot_size * truncated_slots); - - // The slots that do not contain discarded pages should not be included to - // |truncated_slots|. Detects those slots and fixes |truncated_slots| and - // |num_slots| accordingly. - uintptr_t rounded_up_truncatation_begin_addr = - RoundUpToSystemPage(begin_addr); - while (begin_addr + slot_size <= rounded_up_truncatation_begin_addr) { - begin_addr += slot_size; - PA_DCHECK(truncated_slots); - --truncated_slots; - ++num_slots; - } - begin_addr = rounded_up_truncatation_begin_addr; - - // We round the end address here up and not down because we're at the end of - // a slot span, so we "own" all the way up the page boundary. - end_addr = RoundUpToSystemPage(end_addr); - PA_DCHECK(end_addr <= slot_span_start + bucket->get_bytes_per_span()); - if (begin_addr < end_addr) { - unprovisioned_bytes = end_addr - begin_addr; - discardable_bytes += unprovisioned_bytes; - } - if (unprovisioned_bytes && discard) { - PA_DCHECK(truncated_slots > 0); - size_t new_unprovisioned_slots = - truncated_slots + slot_span->num_unprovisioned_slots; - PA_DCHECK(new_unprovisioned_slots <= bucket->get_slots_per_span()); - slot_span->num_unprovisioned_slots = new_unprovisioned_slots; - - // Rewrite the freelist. - internal::PartitionFreelistEntry* head = nullptr; - internal::PartitionFreelistEntry* back = head; - size_t num_new_entries = 0; - for (size_t slot_index = 0; slot_index < num_slots; ++slot_index) { - if (slot_usage[slot_index]) { - continue; - } - - auto* entry = PartitionFreelistEntry::EmplaceAndInitNull( - slot_span_start + (slot_size * slot_index)); - if (!head) { - head = entry; - back = entry; - } else { - back->SetNext(entry); - back = entry; - } - num_new_entries++; -#if !BUILDFLAG(IS_WIN) - last_slot = slot_index; -#endif - } - - slot_span->SetFreelistHead(head); - - PA_DCHECK(num_new_entries == num_slots - slot_span->num_allocated_slots); - -#if BUILDFLAG(USE_FREESLOT_BITMAP) - FreeSlotBitmapReset(slot_span_start + (slot_size * num_slots), end_addr, - slot_size); -#endif - - // Discard the memory. - ScopedSyscallTimer timer{root}; - DiscardSystemPages(begin_addr, unprovisioned_bytes); - } - } - - if (slot_size < SystemPageSize()) { - // Returns here because implementing the following steps for smaller slot - // size will need a complicated logic and make the code messy. - return discardable_bytes; - } - - // Next, walk the slots and for any not in use, consider which system pages - // are no longer needed. We can release any system pages back to the system as - // long as we don't interfere with a freelist pointer or an adjacent used - // slot. - for (size_t i = 0; i < num_slots; ++i) { - if (slot_usage[i]) { - continue; - } - - // The first address we can safely discard is just after the freelist - // pointer. There's one quirk: if the freelist pointer is actually nullptr, - // we can discard that pointer value too. - uintptr_t begin_addr = slot_span_start + (i * slot_size); - uintptr_t end_addr = begin_addr + slot_size; - - bool can_discard_free_list_pointer = false; -#if !BUILDFLAG(IS_WIN) - if (i != last_slot) { - begin_addr += sizeof(internal::PartitionFreelistEntry); - } else { - can_discard_free_list_pointer = true; - } -#else - begin_addr += sizeof(internal::PartitionFreelistEntry); -#endif - - uintptr_t rounded_up_begin_addr = RoundUpToSystemPage(begin_addr); - uintptr_t rounded_down_begin_addr = RoundDownToSystemPage(begin_addr); - end_addr = RoundDownToSystemPage(end_addr); - - // |rounded_up_begin_addr| could be greater than |end_addr| only if slot - // size was less than system page size, or if free list pointer crossed the - // page boundary. Neither is possible here. - PA_DCHECK(rounded_up_begin_addr <= end_addr); - - if (rounded_down_begin_addr < rounded_up_begin_addr && i != 0 && - !slot_usage[i - 1] && can_discard_free_list_pointer) { - // This slot contains a partial page in the beginning. The rest of that - // page is contained in the slot[i-1], which is also discardable. - // Therefore we can discard this page. - begin_addr = rounded_down_begin_addr; - } else { - begin_addr = rounded_up_begin_addr; - } - - if (begin_addr < end_addr) { - size_t partial_slot_bytes = end_addr - begin_addr; - discardable_bytes += partial_slot_bytes; - if (discard) { - ScopedSyscallTimer timer{root}; - DiscardSystemPages(begin_addr, partial_slot_bytes); - } - } - } - - return discardable_bytes; -} - -template <bool thread_safe> -static void PartitionPurgeBucket( - internal::PartitionBucket<thread_safe>* bucket) { - if (bucket->active_slot_spans_head != - internal::SlotSpanMetadata<thread_safe>::get_sentinel_slot_span()) { - for (internal::SlotSpanMetadata<thread_safe>* slot_span = - bucket->active_slot_spans_head; - slot_span; slot_span = slot_span->next_slot_span) { - PA_DCHECK( - slot_span != - internal::SlotSpanMetadata<thread_safe>::get_sentinel_slot_span()); - PartitionPurgeSlotSpan(slot_span, true); - } - } -} - -template <bool thread_safe> -static void PartitionDumpSlotSpanStats( - PartitionBucketMemoryStats* stats_out, - internal::SlotSpanMetadata<thread_safe>* slot_span) { - uint16_t bucket_num_slots = slot_span->bucket->get_slots_per_span(); - - if (slot_span->is_decommitted()) { - ++stats_out->num_decommitted_slot_spans; - return; - } - - stats_out->discardable_bytes += PartitionPurgeSlotSpan(slot_span, false); - - if (slot_span->CanStoreRawSize()) { - stats_out->active_bytes += static_cast<uint32_t>(slot_span->GetRawSize()); - } else { - stats_out->active_bytes += - (slot_span->num_allocated_slots * stats_out->bucket_slot_size); - } - stats_out->active_count += slot_span->num_allocated_slots; - - size_t slot_span_bytes_resident = RoundUpToSystemPage( - (bucket_num_slots - slot_span->num_unprovisioned_slots) * - stats_out->bucket_slot_size); - stats_out->resident_bytes += slot_span_bytes_resident; - if (slot_span->is_empty()) { - stats_out->decommittable_bytes += slot_span_bytes_resident; - ++stats_out->num_empty_slot_spans; - } else if (slot_span->is_full()) { - ++stats_out->num_full_slot_spans; - } else { - PA_DCHECK(slot_span->is_active()); - ++stats_out->num_active_slot_spans; - } -} - -template <bool thread_safe> -static void PartitionDumpBucketStats( - PartitionBucketMemoryStats* stats_out, - const internal::PartitionBucket<thread_safe>* bucket) { - PA_DCHECK(!bucket->is_direct_mapped()); - stats_out->is_valid = false; - // If the active slot span list is empty (== - // internal::SlotSpanMetadata::get_sentinel_slot_span()), the bucket might - // still need to be reported if it has a list of empty, decommitted or full - // slot spans. - if (bucket->active_slot_spans_head == - internal::SlotSpanMetadata<thread_safe>::get_sentinel_slot_span() && - !bucket->empty_slot_spans_head && !bucket->decommitted_slot_spans_head && - !bucket->num_full_slot_spans) { - return; - } - - memset(stats_out, '\0', sizeof(*stats_out)); - stats_out->is_valid = true; - stats_out->is_direct_map = false; - stats_out->num_full_slot_spans = - static_cast<size_t>(bucket->num_full_slot_spans); - stats_out->bucket_slot_size = bucket->slot_size; - uint16_t bucket_num_slots = bucket->get_slots_per_span(); - size_t bucket_useful_storage = stats_out->bucket_slot_size * bucket_num_slots; - stats_out->allocated_slot_span_size = bucket->get_bytes_per_span(); - stats_out->active_bytes = bucket->num_full_slot_spans * bucket_useful_storage; - stats_out->active_count = bucket->num_full_slot_spans * bucket_num_slots; - stats_out->resident_bytes = - bucket->num_full_slot_spans * stats_out->allocated_slot_span_size; - - for (internal::SlotSpanMetadata<thread_safe>* slot_span = - bucket->empty_slot_spans_head; - slot_span; slot_span = slot_span->next_slot_span) { - PA_DCHECK(slot_span->is_empty() || slot_span->is_decommitted()); - PartitionDumpSlotSpanStats(stats_out, slot_span); - } - for (internal::SlotSpanMetadata<thread_safe>* slot_span = - bucket->decommitted_slot_spans_head; - slot_span; slot_span = slot_span->next_slot_span) { - PA_DCHECK(slot_span->is_decommitted()); - PartitionDumpSlotSpanStats(stats_out, slot_span); - } - - if (bucket->active_slot_spans_head != - internal::SlotSpanMetadata<thread_safe>::get_sentinel_slot_span()) { - for (internal::SlotSpanMetadata<thread_safe>* slot_span = - bucket->active_slot_spans_head; - slot_span; slot_span = slot_span->next_slot_span) { - PA_DCHECK( - slot_span != - internal::SlotSpanMetadata<thread_safe>::get_sentinel_slot_span()); - PartitionDumpSlotSpanStats(stats_out, slot_span); - } - } -} - -#if BUILDFLAG(PA_DCHECK_IS_ON) -void DCheckIfManagedByPartitionAllocBRPPool(uintptr_t address) { - PA_DCHECK(IsManagedByPartitionAllocBRPPool(address)); -} -#endif - -#if BUILDFLAG(ENABLE_PKEYS) -void PartitionAllocPkeyInit(int pkey) { - PkeySettings::settings.enabled = true; - PartitionAddressSpace::InitPkeyPool(pkey); - // Call TagGlobalsWithPkey last since we might not have write permissions to - // to memory tagged with `pkey` at this point. - TagGlobalsWithPkey(pkey); -} -#endif // BUILDFLAG(ENABLE_PKEYS) - -} // namespace internal - -template <bool thread_safe> -[[noreturn]] PA_NOINLINE void PartitionRoot<thread_safe>::OutOfMemory( - size_t size) { - const size_t virtual_address_space_size = - total_size_of_super_pages.load(std::memory_order_relaxed) + - total_size_of_direct_mapped_pages.load(std::memory_order_relaxed); -#if !defined(ARCH_CPU_64_BITS) - const size_t uncommitted_size = - virtual_address_space_size - - total_size_of_committed_pages.load(std::memory_order_relaxed); - - // Check whether this OOM is due to a lot of super pages that are allocated - // but not committed, probably due to http://crbug.com/421387. - if (uncommitted_size > internal::kReasonableSizeOfUnusedPages) { - internal::PartitionOutOfMemoryWithLotsOfUncommitedPages(size); - } - -#if BUILDFLAG(IS_WIN) - // If true then we are running on 64-bit Windows. - BOOL is_wow_64 = FALSE; - // Intentionally ignoring failures. - IsWow64Process(GetCurrentProcess(), &is_wow_64); - // 32-bit address space on Windows is typically either 2 GiB (on 32-bit - // Windows) or 4 GiB (on 64-bit Windows). 2.8 and 1.0 GiB are just rough - // guesses as to how much address space PA can consume (note that code, - // stacks, and other allocators will also consume address space). - const size_t kReasonableVirtualSize = (is_wow_64 ? 2800 : 1024) * 1024 * 1024; - // Make it obvious whether we are running on 64-bit Windows. - PA_DEBUG_DATA_ON_STACK("is_wow_64", static_cast<size_t>(is_wow_64)); -#else - constexpr size_t kReasonableVirtualSize = - // 1.5GiB elsewhere, since address space is typically 3GiB. - (1024 + 512) * 1024 * 1024; -#endif - if (virtual_address_space_size > kReasonableVirtualSize) { - internal::PartitionOutOfMemoryWithLargeVirtualSize( - virtual_address_space_size); - } -#endif // #if !defined(ARCH_CPU_64_BITS) - - // Out of memory can be due to multiple causes, such as: - // - Out of virtual address space in the desired pool - // - Out of commit due to either our process, or another one - // - Excessive allocations in the current process - // - // Saving these values make it easier to distinguish between these. See the - // documentation in PA_CONFIG(DEBUG_DATA_ON_STACK) on how to get these from - // minidumps. - PA_DEBUG_DATA_ON_STACK("va_size", virtual_address_space_size); - PA_DEBUG_DATA_ON_STACK("alloc", get_total_size_of_allocated_bytes()); - PA_DEBUG_DATA_ON_STACK("commit", get_total_size_of_committed_pages()); - PA_DEBUG_DATA_ON_STACK("size", size); - - if (internal::g_oom_handling_function) { - (*internal::g_oom_handling_function)(size); - } - OOM_CRASH(size); -} - -template <bool thread_safe> -void PartitionRoot<thread_safe>::DecommitEmptySlotSpans() { - ShrinkEmptySlotSpansRing(0); - // Just decommitted everything, and holding the lock, should be exactly 0. - PA_DCHECK(empty_slot_spans_dirty_bytes == 0); -} - -template <bool thread_safe> -void PartitionRoot<thread_safe>::DestructForTesting() { - // We need to destruct the thread cache before we unreserve any of the super - // pages below, which we currently are not doing. So, we should only call - // this function on PartitionRoots without a thread cache. - PA_CHECK(!flags.with_thread_cache); - auto pool_handle = ChoosePool(); -#if BUILDFLAG(ENABLE_PKEYS) - // The pages managed by pkey will be free-ed at UninitPKeyForTesting(). - // Don't invoke FreePages() for the pages. - if (pool_handle == internal::kPkeyPoolHandle) { - return; - } - PA_DCHECK(pool_handle < internal::kNumPools); -#else - PA_DCHECK(pool_handle <= internal::kNumPools); -#endif - - auto* curr = first_extent; - while (curr != nullptr) { - auto* next = curr->next; - uintptr_t address = SuperPagesBeginFromExtent(curr); - size_t size = - internal::kSuperPageSize * curr->number_of_consecutive_super_pages; -#if !BUILDFLAG(HAS_64_BIT_POINTERS) - internal::AddressPoolManager::GetInstance().MarkUnused(pool_handle, address, - size); -#endif - internal::AddressPoolManager::GetInstance().UnreserveAndDecommit( - pool_handle, address, size); - curr = next; - } -} - -#if PA_CONFIG(ENABLE_MAC11_MALLOC_SIZE_HACK) -template <bool thread_safe> -void PartitionRoot<thread_safe>::EnableMac11MallocSizeHackForTesting() { - flags.mac11_malloc_size_hack_enabled_ = true; -} -#endif // PA_CONFIG(ENABLE_MAC11_MALLOC_SIZE_HACK) - -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) && !BUILDFLAG(HAS_64_BIT_POINTERS) -namespace { -std::atomic<bool> g_reserve_brp_guard_region_called; -// An address constructed by repeating `kQuarantinedByte` shouldn't never point -// to valid memory. Preemptively reserve a memory region around that address and -// make it inaccessible. Not needed for 64-bit platforms where the address is -// guaranteed to be non-canonical. Safe to call multiple times. -void ReserveBackupRefPtrGuardRegionIfNeeded() { - bool expected = false; - // No need to block execution for potential concurrent initialization, merely - // want to make sure this is only called once. - if (!g_reserve_brp_guard_region_called.compare_exchange_strong(expected, - true)) { - return; - } - - size_t alignment = internal::PageAllocationGranularity(); - uintptr_t requested_address; - memset(&requested_address, internal::kQuarantinedByte, - sizeof(requested_address)); - requested_address = RoundDownToPageAllocationGranularity(requested_address); - - // Request several pages so that even unreasonably large C++ objects stay - // within the inaccessible region. If some of the pages can't be reserved, - // it's still preferable to try and reserve the rest. - for (size_t i = 0; i < 4; ++i) { - [[maybe_unused]] uintptr_t allocated_address = - AllocPages(requested_address, alignment, alignment, - PageAccessibilityConfiguration( - PageAccessibilityConfiguration::kInaccessible), - PageTag::kPartitionAlloc); - requested_address += alignment; - } -} -} // namespace -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) && - // !BUILDFLAG(HAS_64_BIT_POINTERS) - -template <bool thread_safe> -void PartitionRoot<thread_safe>::Init(PartitionOptions opts) { - { -#if BUILDFLAG(IS_APPLE) - // Needed to statically bound page size, which is a runtime constant on - // apple OSes. - PA_CHECK((internal::SystemPageSize() == (size_t{1} << 12)) || - (internal::SystemPageSize() == (size_t{1} << 14))); -#elif BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64) - // Check runtime pagesize. Though the code is currently the same, it is - // not merged with the IS_APPLE case above as a 1 << 16 case needs to be - // added here in the future, to allow 64 kiB pagesize. That is only - // supported on Linux on arm64, not on IS_APPLE, but not yet present here - // as the rest of the partition allocator does not currently support it. - PA_CHECK((internal::SystemPageSize() == (size_t{1} << 12)) || - (internal::SystemPageSize() == (size_t{1} << 14))); -#endif - - ::partition_alloc::internal::ScopedGuard guard{lock_}; - if (initialized) { - return; - } - - // Swaps out the active no-op tagging intrinsics with MTE-capable ones, if - // running on the right hardware. - ::partition_alloc::internal::InitializeMTESupportIfNeeded(); - -#if BUILDFLAG(HAS_64_BIT_POINTERS) - // Reserve address space for partition alloc. - internal::PartitionAddressSpace::Init(); -#endif - -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) && !BUILDFLAG(HAS_64_BIT_POINTERS) - ReserveBackupRefPtrGuardRegionIfNeeded(); -#endif - - flags.allow_aligned_alloc = - opts.aligned_alloc == PartitionOptions::AlignedAlloc::kAllowed; - flags.allow_cookie = opts.cookie == PartitionOptions::Cookie::kAllowed; -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - flags.brp_enabled_ = - opts.backup_ref_ptr == PartitionOptions::BackupRefPtr::kEnabled; - flags.brp_zapping_enabled_ = - opts.backup_ref_ptr_zapping == - PartitionOptions::BackupRefPtrZapping::kEnabled; - PA_CHECK(!flags.brp_zapping_enabled_ || flags.brp_enabled_); -#if PA_CONFIG(ENABLE_MAC11_MALLOC_SIZE_HACK) && BUILDFLAG(IS_APPLE) - flags.mac11_malloc_size_hack_enabled_ = - flags.brp_enabled_ && internal::base::mac::IsOS11(); -#endif // PA_CONFIG(ENABLE_MAC11_MALLOC_SIZE_HACK) && BUILDFLAG(IS_APPLE) -#else // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - PA_CHECK(opts.backup_ref_ptr == PartitionOptions::BackupRefPtr::kDisabled); -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - flags.use_configurable_pool = - (opts.use_configurable_pool == - PartitionOptions::UseConfigurablePool::kIfAvailable) && - IsConfigurablePoolAvailable(); - PA_DCHECK(!flags.use_configurable_pool || IsConfigurablePoolAvailable()); - - // brp_enabled() is not supported in the configurable pool because - // BRP requires objects to be in a different Pool. - PA_CHECK(!(flags.use_configurable_pool && brp_enabled())); - -#if BUILDFLAG(ENABLE_PKEYS) - // BRP and pkey mode use different pools, so they can't be enabled at the - // same time. - PA_CHECK(opts.pkey == internal::kDefaultPkey || - opts.backup_ref_ptr == PartitionOptions::BackupRefPtr::kDisabled); - flags.pkey = opts.pkey; -#endif - - // Ref-count messes up alignment needed for AlignedAlloc, making this - // option incompatible. However, except in the - // PUT_REF_COUNT_IN_PREVIOUS_SLOT case. -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) && \ - !BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) - PA_CHECK(!flags.allow_aligned_alloc || !flags.brp_enabled_); -#endif - -#if PA_CONFIG(EXTRAS_REQUIRED) - flags.extras_size = 0; - flags.extras_offset = 0; - - if (flags.allow_cookie) { - flags.extras_size += internal::kPartitionCookieSizeAdjustment; - } - - if (brp_enabled()) { - // TODO(tasak): In the PUT_REF_COUNT_IN_PREVIOUS_SLOT case, ref-count is - // stored out-of-line for single-slot slot spans, so no need to - // add/subtract its size in this case. - flags.extras_size += internal::kPartitionRefCountSizeAdjustment; - flags.extras_offset += internal::kPartitionRefCountOffsetAdjustment; - } - if (opts.add_dummy_ref_count == - PartitionOptions::AddDummyRefCount::kEnabled) { - // AddDummyRefCount will increase the size to simulate adding - // PartitionRefCount, but non of the BRP logic will run. - PA_CHECK(!brp_enabled()); - flags.extras_size += internal::kPartitionRefCountSizeAdjustment; - } -#endif // PA_CONFIG(EXTRAS_REQUIRED) - - // Re-confirm the above PA_CHECKs, by making sure there are no - // pre-allocation extras when AlignedAlloc is allowed. Post-allocation - // extras are ok. - PA_CHECK(!flags.allow_aligned_alloc || !flags.extras_offset); - - flags.quarantine_mode = -#if BUILDFLAG(USE_STARSCAN) - (opts.quarantine == PartitionOptions::Quarantine::kDisallowed - ? QuarantineMode::kAlwaysDisabled - : QuarantineMode::kDisabledByDefault); -#else - QuarantineMode::kAlwaysDisabled; -#endif // BUILDFLAG(USE_STARSCAN) - - // We mark the sentinel slot span as free to make sure it is skipped by our - // logic to find a new active slot span. - memset(&sentinel_bucket, 0, sizeof(sentinel_bucket)); - sentinel_bucket.active_slot_spans_head = - SlotSpan::get_sentinel_slot_span_non_const(); - - // This is a "magic" value so we can test if a root pointer is valid. - inverted_self = ~reinterpret_cast<uintptr_t>(this); - - // Set up the actual usable buckets first. - constexpr internal::BucketIndexLookup lookup{}; - size_t bucket_index = 0; - while (lookup.bucket_sizes()[bucket_index] != - internal::kInvalidBucketSize) { - buckets[bucket_index].Init(lookup.bucket_sizes()[bucket_index]); - bucket_index++; - } - PA_DCHECK(bucket_index < internal::kNumBuckets); - - // Remaining buckets are not usable, and not real. - for (size_t index = bucket_index; index < internal::kNumBuckets; index++) { - // Cannot init with size 0 since it computes 1 / size, but make sure the - // bucket is invalid. - buckets[index].Init(internal::kInvalidBucketSize); - buckets[index].active_slot_spans_head = nullptr; - PA_DCHECK(!buckets[index].is_valid()); - } - -#if !PA_CONFIG(THREAD_CACHE_SUPPORTED) - // TLS in ThreadCache not supported on other OSes. - flags.with_thread_cache = false; -#else - ThreadCache::EnsureThreadSpecificDataInitialized(); - flags.with_thread_cache = - (opts.thread_cache == PartitionOptions::ThreadCache::kEnabled); - - if (flags.with_thread_cache) { - ThreadCache::Init(this); - } -#endif // !PA_CONFIG(THREAD_CACHE_SUPPORTED) - -#if PA_CONFIG(USE_PARTITION_ROOT_ENUMERATOR) - internal::PartitionRootEnumerator::Instance().Register(this); -#endif - - initialized = true; - } - - // Called without the lock, might allocate. -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - PartitionAllocMallocInitOnce(); -#endif - -#if BUILDFLAG(ENABLE_PKEYS) - if (flags.pkey != internal::kDefaultPkey) { - internal::PartitionAllocPkeyInit(flags.pkey); - } -#endif -} - -template <bool thread_safe> -PartitionRoot<thread_safe>::~PartitionRoot() { -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - PA_CHECK(!flags.with_thread_cache) - << "Must not destroy a partition with a thread cache"; -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - -#if PA_CONFIG(USE_PARTITION_ROOT_ENUMERATOR) - if (initialized) { - internal::PartitionRootEnumerator::Instance().Unregister(this); - } -#endif // PA_CONFIG(USE_PARTITION_ALLOC_ENUMERATOR) -} - -template <bool thread_safe> -void PartitionRoot<thread_safe>::EnableThreadCacheIfSupported() { -#if PA_CONFIG(THREAD_CACHE_SUPPORTED) - ::partition_alloc::internal::ScopedGuard guard{lock_}; - PA_CHECK(!flags.with_thread_cache); - // By the time we get there, there may be multiple threads created in the - // process. Since `with_thread_cache` is accessed without a lock, it can - // become visible to another thread before the effects of - // `internal::ThreadCacheInit()` are visible. To prevent that, we fake thread - // cache creation being in-progress while this is running. - // - // This synchronizes with the acquire load in `MaybeInitThreadCacheAndAlloc()` - // to ensure that we don't create (and thus use) a ThreadCache before - // ThreadCache::Init()'s effects are visible. - int before = - thread_caches_being_constructed_.fetch_add(1, std::memory_order_acquire); - PA_CHECK(before == 0); - ThreadCache::Init(this); - thread_caches_being_constructed_.fetch_sub(1, std::memory_order_release); - flags.with_thread_cache = true; -#endif // PA_CONFIG(THREAD_CACHE_SUPPORTED) -} - -template <bool thread_safe> -bool PartitionRoot<thread_safe>::TryReallocInPlaceForDirectMap( - internal::SlotSpanMetadata<thread_safe>* slot_span, - size_t requested_size) { - PA_DCHECK(slot_span->bucket->is_direct_mapped()); - // Slot-span metadata isn't MTE-tagged. - PA_DCHECK( - internal::IsManagedByDirectMap(reinterpret_cast<uintptr_t>(slot_span))); - - size_t raw_size = AdjustSizeForExtrasAdd(requested_size); - auto* extent = DirectMapExtent::FromSlotSpan(slot_span); - size_t current_reservation_size = extent->reservation_size; - // Calculate the new reservation size the way PartitionDirectMap() would, but - // skip the alignment, because this call isn't requesting it. - size_t new_reservation_size = GetDirectMapReservationSize(raw_size); - - // If new reservation would be larger, there is nothing we can do to - // reallocate in-place. - if (new_reservation_size > current_reservation_size) { - return false; - } - - // Don't reallocate in-place if new reservation size would be less than 80 % - // of the current one, to avoid holding on to too much unused address space. - // Make this check before comparing slot sizes, as even with equal or similar - // slot sizes we can save a lot if the original allocation was heavily padded - // for alignment. - if ((new_reservation_size >> internal::SystemPageShift()) * 5 < - (current_reservation_size >> internal::SystemPageShift()) * 4) { - return false; - } - - // Note that the new size isn't a bucketed size; this function is called - // whenever we're reallocating a direct mapped allocation, so calculate it - // the way PartitionDirectMap() would. - size_t new_slot_size = GetDirectMapSlotSize(raw_size); - if (new_slot_size < internal::kMinDirectMappedDownsize) { - return false; - } - - // Past this point, we decided we'll attempt to reallocate without relocating, - // so we have to honor the padding for alignment in front of the original - // allocation, even though this function isn't requesting any alignment. - - // bucket->slot_size is the currently committed size of the allocation. - size_t current_slot_size = slot_span->bucket->slot_size; - size_t current_usable_size = slot_span->GetUsableSize(this); - uintptr_t slot_start = SlotSpan::ToSlotSpanStart(slot_span); - // This is the available part of the reservation up to which the new - // allocation can grow. - size_t available_reservation_size = - current_reservation_size - extent->padding_for_alignment - - PartitionRoot<thread_safe>::GetDirectMapMetadataAndGuardPagesSize(); -#if BUILDFLAG(PA_DCHECK_IS_ON) - uintptr_t reservation_start = slot_start & internal::kSuperPageBaseMask; - PA_DCHECK(internal::IsReservationStart(reservation_start)); - PA_DCHECK(slot_start + available_reservation_size == - reservation_start + current_reservation_size - - GetDirectMapMetadataAndGuardPagesSize() + - internal::PartitionPageSize()); -#endif - - if (new_slot_size == current_slot_size) { - // No need to move any memory around, but update size and cookie below. - // That's because raw_size may have changed. - } else if (new_slot_size < current_slot_size) { - // Shrink by decommitting unneeded pages and making them inaccessible. - size_t decommit_size = current_slot_size - new_slot_size; - DecommitSystemPagesForData(slot_start + new_slot_size, decommit_size, - PageAccessibilityDisposition::kRequireUpdate); - // Since the decommited system pages are still reserved, we don't need to - // change the entries for decommitted pages in the reservation offset table. - } else if (new_slot_size <= available_reservation_size) { - // Grow within the actually reserved address space. Just need to make the - // pages accessible again. - size_t recommit_slot_size_growth = new_slot_size - current_slot_size; - RecommitSystemPagesForData(slot_start + current_slot_size, - recommit_slot_size_growth, - PageAccessibilityDisposition::kRequireUpdate); - // The recommited system pages had been already reserved and all the - // entries in the reservation offset table (for entire reservation_size - // region) have been already initialized. - -#if BUILDFLAG(PA_DCHECK_IS_ON) - memset(reinterpret_cast<void*>(slot_start + current_slot_size), - internal::kUninitializedByte, recommit_slot_size_growth); -#endif - } else { - // We can't perform the realloc in-place. - // TODO: support this too when possible. - return false; - } - - DecreaseTotalSizeOfAllocatedBytes(reinterpret_cast<uintptr_t>(slot_span), - slot_span->bucket->slot_size); - slot_span->SetRawSize(raw_size); - slot_span->bucket->slot_size = new_slot_size; - IncreaseTotalSizeOfAllocatedBytes(reinterpret_cast<uintptr_t>(slot_span), - slot_span->bucket->slot_size, raw_size); - - // Always record in-place realloc() as free()+malloc() pair. - // - // The early returns above (`return false`) will fall back to free()+malloc(), - // so this is consistent. - auto* thread_cache = GetOrCreateThreadCache(); - if (ThreadCache::IsValid(thread_cache)) { - thread_cache->RecordDeallocation(current_usable_size); - thread_cache->RecordAllocation(slot_span->GetUsableSize(this)); - } - -#if BUILDFLAG(PA_DCHECK_IS_ON) - // Write a new trailing cookie. - if (flags.allow_cookie) { - auto* object = static_cast<unsigned char*>(SlotStartToObject(slot_start)); - internal::PartitionCookieWriteValue(object + - slot_span->GetUsableSize(this)); - } -#endif - - return true; -} - -template <bool thread_safe> -bool PartitionRoot<thread_safe>::TryReallocInPlaceForNormalBuckets( - void* object, - SlotSpan* slot_span, - size_t new_size) { - uintptr_t slot_start = ObjectToSlotStart(object); - PA_DCHECK(internal::IsManagedByNormalBuckets(slot_start)); - - // TODO: note that tcmalloc will "ignore" a downsizing realloc() unless the - // new size is a significant percentage smaller. We could do the same if we - // determine it is a win. - if (AllocationCapacityFromRequestedSize(new_size) != - AllocationCapacityFromSlotStart(slot_start)) { - return false; - } - size_t current_usable_size = slot_span->GetUsableSize(this); - - // Trying to allocate |new_size| would use the same amount of underlying - // memory as we're already using, so re-use the allocation after updating - // statistics (and cookie, if present). - if (slot_span->CanStoreRawSize()) { -#if BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) && BUILDFLAG(PA_DCHECK_IS_ON) - internal::PartitionRefCount* old_ref_count; - if (brp_enabled()) { - old_ref_count = internal::PartitionRefCountPointer(slot_start); - } -#endif // BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) && - // BUILDFLAG(PA_DCHECK_IS_ON) - size_t new_raw_size = AdjustSizeForExtrasAdd(new_size); - slot_span->SetRawSize(new_raw_size); -#if BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) && BUILDFLAG(PA_DCHECK_IS_ON) - if (brp_enabled()) { - internal::PartitionRefCount* new_ref_count = - internal::PartitionRefCountPointer(slot_start); - PA_DCHECK(new_ref_count == old_ref_count); - } -#endif // BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) && - // BUILDFLAG(PA_DCHECK_IS_ON) -#if BUILDFLAG(PA_DCHECK_IS_ON) - // Write a new trailing cookie only when it is possible to keep track - // raw size (otherwise we wouldn't know where to look for it later). - if (flags.allow_cookie) { - internal::PartitionCookieWriteValue(static_cast<unsigned char*>(object) + - slot_span->GetUsableSize(this)); - } -#endif // BUILDFLAG(PA_DCHECK_IS_ON) - } - - // Always record a realloc() as a free() + malloc(), even if it's in - // place. When we cannot do it in place (`return false` above), the allocator - // falls back to free()+malloc(), so this is consistent. - ThreadCache* thread_cache = GetOrCreateThreadCache(); - if (PA_LIKELY(ThreadCache::IsValid(thread_cache))) { - thread_cache->RecordDeallocation(current_usable_size); - thread_cache->RecordAllocation(slot_span->GetUsableSize(this)); - } - - return object; -} - -template <bool thread_safe> -void* PartitionRoot<thread_safe>::ReallocWithFlags(unsigned int flags, - void* ptr, - size_t new_size, - const char* type_name) { -#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - CHECK_MAX_SIZE_OR_RETURN_NULLPTR(new_size, flags); - void* result = realloc(ptr, new_size); - PA_CHECK(result || flags & AllocFlags::kReturnNull); - return result; -#else - bool no_hooks = flags & AllocFlags::kNoHooks; - if (PA_UNLIKELY(!ptr)) { - return no_hooks - ? AllocWithFlagsNoHooks(flags, new_size, - internal::PartitionPageSize()) - : AllocWithFlagsInternal( - flags, new_size, internal::PartitionPageSize(), type_name); - } - - if (PA_UNLIKELY(!new_size)) { - Free(ptr); - return nullptr; - } - - if (new_size > internal::MaxDirectMapped()) { - if (flags & AllocFlags::kReturnNull) { - return nullptr; - } - internal::PartitionExcessiveAllocationSize(new_size); - } - - const bool hooks_enabled = PartitionAllocHooks::AreHooksEnabled(); - bool overridden = false; - size_t old_usable_size; - if (PA_UNLIKELY(!no_hooks && hooks_enabled)) { - overridden = PartitionAllocHooks::ReallocOverrideHookIfEnabled( - &old_usable_size, ptr); - } - if (PA_LIKELY(!overridden)) { - // |ptr| may have been allocated in another root. - SlotSpan* slot_span = SlotSpan::FromObject(ptr); - auto* old_root = PartitionRoot::FromSlotSpan(slot_span); - bool success = false; - bool tried_in_place_for_direct_map = false; - { - ::partition_alloc::internal::ScopedGuard guard{old_root->lock_}; - // TODO(crbug.com/1257655): See if we can afford to make this a CHECK. - PA_DCHECK(IsValidSlotSpan(slot_span)); - old_usable_size = slot_span->GetUsableSize(old_root); - - if (PA_UNLIKELY(slot_span->bucket->is_direct_mapped())) { - tried_in_place_for_direct_map = true; - // We may be able to perform the realloc in place by changing the - // accessibility of memory pages and, if reducing the size, decommitting - // them. - success = old_root->TryReallocInPlaceForDirectMap(slot_span, new_size); - } - } - if (success) { - if (PA_UNLIKELY(!no_hooks && hooks_enabled)) { - PartitionAllocHooks::ReallocObserverHookIfEnabled(ptr, ptr, new_size, - type_name); - } - return ptr; - } - - if (PA_LIKELY(!tried_in_place_for_direct_map)) { - if (old_root->TryReallocInPlaceForNormalBuckets(ptr, slot_span, - new_size)) { - return ptr; - } - } - } - - // This realloc cannot be resized in-place. Sadness. - void* ret = - no_hooks ? AllocWithFlagsNoHooks(flags, new_size, - internal::PartitionPageSize()) - : AllocWithFlagsInternal( - flags, new_size, internal::PartitionPageSize(), type_name); - if (!ret) { - if (flags & AllocFlags::kReturnNull) { - return nullptr; - } - internal::PartitionExcessiveAllocationSize(new_size); - } - - memcpy(ret, ptr, std::min(old_usable_size, new_size)); - Free(ptr); // Implicitly protects the old ptr on MTE systems. - return ret; -#endif -} - -template <bool thread_safe> -void PartitionRoot<thread_safe>::PurgeMemory(int flags) { - { - ::partition_alloc::internal::ScopedGuard guard{lock_}; -#if BUILDFLAG(USE_STARSCAN) - // Avoid purging if there is PCScan task currently scheduled. Since pcscan - // takes snapshot of all allocated pages, decommitting pages here (even - // under the lock) is racy. - // TODO(bikineev): Consider rescheduling the purging after PCScan. - if (PCScan::IsInProgress()) { - return; - } -#endif // BUILDFLAG(USE_STARSCAN) - - if (flags & PurgeFlags::kDecommitEmptySlotSpans) { - DecommitEmptySlotSpans(); - } - if (flags & PurgeFlags::kDiscardUnusedSystemPages) { - for (Bucket& bucket : buckets) { - if (bucket.slot_size == internal::kInvalidBucketSize) { - continue; - } - - if (bucket.slot_size >= internal::MinPurgeableSlotSize()) { - internal::PartitionPurgeBucket(&bucket); - } else { - bucket.SortSlotSpanFreelists(); - } - - // Do it at the end, as the actions above change the status of slot - // spans (e.g. empty -> decommitted). - bucket.MaintainActiveList(); - - if (sort_active_slot_spans_) { - bucket.SortActiveSlotSpans(); - } - } - } - } -} - -template <bool thread_safe> -void PartitionRoot<thread_safe>::ShrinkEmptySlotSpansRing(size_t limit) { - int16_t index = global_empty_slot_span_ring_index; - int16_t starting_index = index; - while (empty_slot_spans_dirty_bytes > limit) { - SlotSpan* slot_span = global_empty_slot_span_ring[index]; - // The ring is not always full, may be nullptr. - if (slot_span) { - slot_span->DecommitIfPossible(this); - global_empty_slot_span_ring[index] = nullptr; - } - index += 1; - // Walk through the entirety of possible slots, even though the last ones - // are unused, if global_empty_slot_span_ring_size is smaller than - // kMaxFreeableSpans. It's simpler, and does not cost anything, since all - // the pointers are going to be nullptr. - if (index == internal::kMaxFreeableSpans) { - index = 0; - } - - // Went around the whole ring, since this is locked, - // empty_slot_spans_dirty_bytes should be exactly 0. - if (index == starting_index) { - PA_DCHECK(empty_slot_spans_dirty_bytes == 0); - // Metrics issue, don't crash, return. - break; - } - } -} - -template <bool thread_safe> -void PartitionRoot<thread_safe>::DumpStats(const char* partition_name, - bool is_light_dump, - PartitionStatsDumper* dumper) { - static const size_t kMaxReportableDirectMaps = 4096; - // Allocate on the heap rather than on the stack to avoid stack overflow - // skirmishes (on Windows, in particular). Allocate before locking below, - // otherwise when PartitionAlloc is malloc() we get reentrancy issues. This - // inflates reported values a bit for detailed dumps though, by 16kiB. - std::unique_ptr<uint32_t[]> direct_map_lengths; - if (!is_light_dump) { - direct_map_lengths = - std::unique_ptr<uint32_t[]>(new uint32_t[kMaxReportableDirectMaps]); - } - PartitionBucketMemoryStats bucket_stats[internal::kNumBuckets]; - size_t num_direct_mapped_allocations = 0; - PartitionMemoryStats stats = {0}; - - stats.syscall_count = syscall_count.load(std::memory_order_relaxed); - stats.syscall_total_time_ns = - syscall_total_time_ns.load(std::memory_order_relaxed); - - // Collect data with the lock held, cannot allocate or call third-party code - // below. - { - ::partition_alloc::internal::ScopedGuard guard{lock_}; - PA_DCHECK(total_size_of_allocated_bytes <= max_size_of_allocated_bytes); - - stats.total_mmapped_bytes = - total_size_of_super_pages.load(std::memory_order_relaxed) + - total_size_of_direct_mapped_pages.load(std::memory_order_relaxed); - stats.total_committed_bytes = - total_size_of_committed_pages.load(std::memory_order_relaxed); - stats.max_committed_bytes = - max_size_of_committed_pages.load(std::memory_order_relaxed); - stats.total_allocated_bytes = total_size_of_allocated_bytes; - stats.max_allocated_bytes = max_size_of_allocated_bytes; -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - stats.total_brp_quarantined_bytes = - total_size_of_brp_quarantined_bytes.load(std::memory_order_relaxed); - stats.total_brp_quarantined_count = - total_count_of_brp_quarantined_slots.load(std::memory_order_relaxed); - stats.cumulative_brp_quarantined_bytes = - cumulative_size_of_brp_quarantined_bytes.load( - std::memory_order_relaxed); - stats.cumulative_brp_quarantined_count = - cumulative_count_of_brp_quarantined_slots.load( - std::memory_order_relaxed); -#endif - - size_t direct_mapped_allocations_total_size = 0; - for (size_t i = 0; i < internal::kNumBuckets; ++i) { - const Bucket* bucket = &bucket_at(i); - // Don't report the pseudo buckets that the generic allocator sets up in - // order to preserve a fast size->bucket map (see - // PartitionRoot::Init() for details). - if (!bucket->is_valid()) { - bucket_stats[i].is_valid = false; - } else { - internal::PartitionDumpBucketStats(&bucket_stats[i], bucket); - } - if (bucket_stats[i].is_valid) { - stats.total_resident_bytes += bucket_stats[i].resident_bytes; - stats.total_active_bytes += bucket_stats[i].active_bytes; - stats.total_active_count += bucket_stats[i].active_count; - stats.total_decommittable_bytes += bucket_stats[i].decommittable_bytes; - stats.total_discardable_bytes += bucket_stats[i].discardable_bytes; - } - } - - for (DirectMapExtent* extent = direct_map_list; - extent && num_direct_mapped_allocations < kMaxReportableDirectMaps; - extent = extent->next_extent, ++num_direct_mapped_allocations) { - PA_DCHECK(!extent->next_extent || - extent->next_extent->prev_extent == extent); - size_t slot_size = extent->bucket->slot_size; - direct_mapped_allocations_total_size += slot_size; - if (is_light_dump) { - continue; - } - direct_map_lengths[num_direct_mapped_allocations] = slot_size; - } - - stats.total_resident_bytes += direct_mapped_allocations_total_size; - stats.total_active_bytes += direct_mapped_allocations_total_size; - stats.total_active_count += num_direct_mapped_allocations; - - stats.has_thread_cache = flags.with_thread_cache; - if (stats.has_thread_cache) { - ThreadCacheRegistry::Instance().DumpStats( - true, &stats.current_thread_cache_stats); - ThreadCacheRegistry::Instance().DumpStats(false, - &stats.all_thread_caches_stats); - } - } - - // Do not hold the lock when calling |dumper|, as it may allocate. - if (!is_light_dump) { - for (auto& stat : bucket_stats) { - if (stat.is_valid) { - dumper->PartitionsDumpBucketStats(partition_name, &stat); - } - } - - for (size_t i = 0; i < num_direct_mapped_allocations; ++i) { - uint32_t size = direct_map_lengths[i]; - - PartitionBucketMemoryStats mapped_stats = {}; - mapped_stats.is_valid = true; - mapped_stats.is_direct_map = true; - mapped_stats.num_full_slot_spans = 1; - mapped_stats.allocated_slot_span_size = size; - mapped_stats.bucket_slot_size = size; - mapped_stats.active_bytes = size; - mapped_stats.active_count = 1; - mapped_stats.resident_bytes = size; - dumper->PartitionsDumpBucketStats(partition_name, &mapped_stats); - } - } - dumper->PartitionDumpTotals(partition_name, &stats); -} - -// static -template <bool thread_safe> -void PartitionRoot<thread_safe>::DeleteForTesting( - PartitionRoot* partition_root) { - if (partition_root->flags.with_thread_cache) { - ThreadCache::SwapForTesting(nullptr); - partition_root->flags.with_thread_cache = false; - } - - partition_root->DestructForTesting(); // IN-TEST - - delete partition_root; -} - -template <bool thread_safe> -void PartitionRoot<thread_safe>::ResetForTesting(bool allow_leaks) { - if (flags.with_thread_cache) { - ThreadCache::SwapForTesting(nullptr); - flags.with_thread_cache = false; - } - - ::partition_alloc::internal::ScopedGuard guard(lock_); - -#if BUILDFLAG(PA_DCHECK_IS_ON) - if (!allow_leaks) { - unsigned num_allocated_slots = 0; - for (Bucket& bucket : buckets) { - if (bucket.active_slot_spans_head != - internal::SlotSpanMetadata<thread_safe>::get_sentinel_slot_span()) { - for (internal::SlotSpanMetadata<thread_safe>* slot_span = - bucket.active_slot_spans_head; - slot_span; slot_span = slot_span->next_slot_span) { - num_allocated_slots += slot_span->num_allocated_slots; - } - } - // Full slot spans are nowhere. Need to see bucket.num_full_slot_spans - // to count the number of full slot spans' slots. - if (bucket.num_full_slot_spans) { - num_allocated_slots += - bucket.num_full_slot_spans * bucket.get_slots_per_span(); - } - } - PA_DCHECK(num_allocated_slots == 0); - - // Check for direct-mapped allocations. - PA_DCHECK(!direct_map_list); - } -#endif - - DestructForTesting(); // IN-TEST - -#if PA_CONFIG(USE_PARTITION_ROOT_ENUMERATOR) - if (initialized) { - internal::PartitionRootEnumerator::Instance().Unregister(this); - } -#endif // PA_CONFIG(USE_PARTITION_ROOT_ENUMERATOR) - - for (Bucket& bucket : buckets) { - bucket.active_slot_spans_head = - SlotSpan::get_sentinel_slot_span_non_const(); - bucket.empty_slot_spans_head = nullptr; - bucket.decommitted_slot_spans_head = nullptr; - bucket.num_full_slot_spans = 0; - } - - next_super_page = 0; - next_partition_page = 0; - next_partition_page_end = 0; - current_extent = nullptr; - first_extent = nullptr; - - direct_map_list = nullptr; - for (auto& entity : global_empty_slot_span_ring) { - entity = nullptr; - } - - global_empty_slot_span_ring_index = 0; - global_empty_slot_span_ring_size = internal::kDefaultEmptySlotSpanRingSize; - initialized = false; -} - -template <bool thread_safe> -void PartitionRoot<thread_safe>::ResetBookkeepingForTesting() { - ::partition_alloc::internal::ScopedGuard guard{lock_}; - max_size_of_allocated_bytes = total_size_of_allocated_bytes; - max_size_of_committed_pages.store(total_size_of_committed_pages); -} - -template <> -ThreadCache* PartitionRoot<internal::ThreadSafe>::MaybeInitThreadCache() { - auto* tcache = ThreadCache::Get(); - // See comment in `EnableThreadCacheIfSupport()` for why this is an acquire - // load. - if (ThreadCache::IsTombstone(tcache) || - thread_caches_being_constructed_.load(std::memory_order_acquire)) { - // Two cases: - // 1. Thread is being terminated, don't try to use the thread cache, and - // don't try to resurrect it. - // 2. Someone, somewhere is currently allocating a thread cache. This may - // be us, in which case we are re-entering and should not create a thread - // cache. If it is not us, then this merely delays thread cache - // construction a bit, which is not an issue. - return nullptr; - } - - // There is no per-thread ThreadCache allocated here yet, and this partition - // has a thread cache, allocate a new one. - // - // The thread cache allocation itself will not reenter here, as it sidesteps - // the thread cache by using placement new and |RawAlloc()|. However, - // internally to libc, allocations may happen to create a new TLS - // variable. This would end up here again, which is not what we want (and - // likely is not supported by libc). - // - // To avoid this sort of reentrancy, increase the count of thread caches that - // are currently allocating a thread cache. - // - // Note that there is no deadlock or data inconsistency concern, since we do - // not hold the lock, and has such haven't touched any internal data. - int before = - thread_caches_being_constructed_.fetch_add(1, std::memory_order_relaxed); - PA_CHECK(before < std::numeric_limits<int>::max()); - tcache = ThreadCache::Create(this); - thread_caches_being_constructed_.fetch_sub(1, std::memory_order_relaxed); - - return tcache; -} - -template <> -void PartitionRoot<internal::ThreadSafe>::EnableSortActiveSlotSpans() { - sort_active_slot_spans_ = true; -} - -template struct PA_COMPONENT_EXPORT(PARTITION_ALLOC) - PartitionRoot<internal::ThreadSafe>; - -static_assert(offsetof(PartitionRoot<internal::ThreadSafe>, sentinel_bucket) == - offsetof(PartitionRoot<internal::ThreadSafe>, buckets) + - internal::kNumBuckets * - sizeof(PartitionRoot<internal::ThreadSafe>::Bucket), - "sentinel_bucket must be just after the regular buckets."); - -static_assert( - offsetof(PartitionRoot<internal::ThreadSafe>, lock_) >= 64, - "The lock should not be on the same cacheline as the read-mostly flags"); - -} // namespace partition_alloc
diff --git a/base/allocator/partition_allocator/partition_root.h b/base/allocator/partition_allocator/partition_root.h deleted file mode 100644 index c1a797a..0000000 --- a/base/allocator/partition_allocator/partition_root.h +++ /dev/null
@@ -1,2249 +0,0 @@ -// 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 BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ROOT_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ROOT_H_ - -// DESCRIPTION -// PartitionRoot::Alloc() and PartitionRoot::Free() are approximately analogous -// to malloc() and free(). -// -// The main difference is that a PartitionRoot object must be supplied to these -// functions, representing a specific "heap partition" that will be used to -// satisfy the allocation. Different partitions are guaranteed to exist in -// separate address spaces, including being separate from the main system -// heap. If the contained objects are all freed, physical memory is returned to -// the system but the address space remains reserved. See PartitionAlloc.md for -// other security properties PartitionAlloc provides. -// -// THE ONLY LEGITIMATE WAY TO OBTAIN A PartitionRoot IS THROUGH THE -// PartitionAllocator classes. To minimize the instruction count to the fullest -// extent possible, the PartitionRoot is really just a header adjacent to other -// data areas provided by the allocator class. -// -// The constraints for PartitionRoot::Alloc() are: -// - Multi-threaded use against a single partition is ok; locking is handled. -// - Allocations of any arbitrary size can be handled (subject to a limit of -// INT_MAX bytes for security reasons). -// - Bucketing is by approximate size, for example an allocation of 4000 bytes -// might be placed into a 4096-byte bucket. Bucket sizes are chosen to try and -// keep worst-case waste to ~10%. - -#include <algorithm> -#include <atomic> -#include <cstddef> -#include <cstdint> - -#include "base/allocator/partition_allocator/address_pool_manager_types.h" -#include "base/allocator/partition_allocator/allocation_guard.h" -#include "base/allocator/partition_allocator/chromecast_buildflags.h" -#include "base/allocator/partition_allocator/freeslot_bitmap.h" -#include "base/allocator/partition_allocator/page_allocator.h" -#include "base/allocator/partition_allocator/partition_address_space.h" -#include "base/allocator/partition_allocator/partition_alloc-inl.h" -#include "base/allocator/partition_allocator/partition_alloc_base/bits.h" -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_base/thread_annotations.h" -#include "base/allocator/partition_allocator/partition_alloc_base/time/time.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/partition_alloc_config.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "base/allocator/partition_allocator/partition_alloc_forward.h" -#include "base/allocator/partition_allocator/partition_alloc_hooks.h" -#include "base/allocator/partition_allocator/partition_alloc_notreached.h" -#include "base/allocator/partition_allocator/partition_bucket_lookup.h" -#include "base/allocator/partition_allocator/partition_cookie.h" -#include "base/allocator/partition_allocator/partition_direct_map_extent.h" -#include "base/allocator/partition_allocator/partition_freelist_entry.h" -#include "base/allocator/partition_allocator/partition_lock.h" -#include "base/allocator/partition_allocator/partition_oom.h" -#include "base/allocator/partition_allocator/partition_page.h" -#include "base/allocator/partition_allocator/partition_ref_count.h" -#include "base/allocator/partition_allocator/pkey.h" -#include "base/allocator/partition_allocator/reservation_offset_table.h" -#include "base/allocator/partition_allocator/tagging.h" -#include "base/allocator/partition_allocator/thread_cache.h" -#include "build/build_config.h" - -#if BUILDFLAG(USE_STARSCAN) -#include "base/allocator/partition_allocator/starscan/pcscan.h" -#endif - -// We use this to make MEMORY_TOOL_REPLACES_ALLOCATOR behave the same for max -// size as other alloc code. -#define CHECK_MAX_SIZE_OR_RETURN_NULLPTR(size, flags) \ - if (size > partition_alloc::internal::MaxDirectMapped()) { \ - if (flags & AllocFlags::kReturnNull) { \ - return nullptr; \ - } \ - PA_CHECK(false); \ - } - -namespace partition_alloc::internal { - -// We want this size to be big enough that we have time to start up other -// scripts _before_ we wrap around. -static constexpr size_t kAllocInfoSize = 1 << 24; - -struct AllocInfo { - std::atomic<size_t> index{0}; - struct { - uintptr_t addr; - size_t size; - } allocs[kAllocInfoSize] = {}; -}; - -#if BUILDFLAG(RECORD_ALLOC_INFO) -extern AllocInfo g_allocs; - -void RecordAllocOrFree(uintptr_t addr, size_t size); -#endif // BUILDFLAG(RECORD_ALLOC_INFO) -} // namespace partition_alloc::internal - -namespace partition_alloc { - -namespace internal { -// Avoid including partition_address_space.h from this .h file, by moving the -// call to IsManagedByPartitionAllocBRPPool into the .cc file. -#if BUILDFLAG(PA_DCHECK_IS_ON) -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -void DCheckIfManagedByPartitionAllocBRPPool(uintptr_t address); -#else -PA_ALWAYS_INLINE void DCheckIfManagedByPartitionAllocBRPPool( - uintptr_t address) {} -#endif - -#if PA_CONFIG(USE_PARTITION_ROOT_ENUMERATOR) -class PartitionRootEnumerator; -#endif - -} // namespace internal - -// Bit flag constants used to purge memory. See PartitionRoot::PurgeMemory. -// -// In order to support bit operations like `flag_a | flag_b`, the old-fashioned -// enum (+ surrounding named struct) is used instead of enum class. -struct PurgeFlags { - enum : int { - // Decommitting the ring list of empty slot spans is reasonably fast. - kDecommitEmptySlotSpans = 1 << 0, - // Discarding unused system pages is slower, because it involves walking all - // freelists in all active slot spans of all buckets >= system page - // size. It often frees a similar amount of memory to decommitting the empty - // slot spans, though. - kDiscardUnusedSystemPages = 1 << 1, - // Aggressively reclaim memory. This is meant to be used in low-memory - // situations, not for periodic memory reclaiming. - kAggressiveReclaim = 1 << 2, - }; -}; - -// Options struct used to configure PartitionRoot and PartitionAllocator. -struct PartitionOptions { - enum class AlignedAlloc : uint8_t { - // By default all allocations will be aligned to `kAlignment`, - // likely to be 8B or 16B depending on platforms and toolchains. - // AlignedAlloc() allows to enforce higher alignment. - // This option determines whether it is supported for the partition. - // Allowing AlignedAlloc() comes at a cost of disallowing extras in front - // of the allocation. - kDisallowed, - kAllowed, - }; - - enum class ThreadCache : uint8_t { - kDisabled, - kEnabled, - }; - - enum class Quarantine : uint8_t { - kDisallowed, - kAllowed, - }; - - enum class Cookie : uint8_t { - kDisallowed, - kAllowed, - }; - - enum class BackupRefPtr : uint8_t { - kDisabled, - kEnabled, - }; - - enum class BackupRefPtrZapping : uint8_t { - kDisabled, - kEnabled, - }; - - enum class AddDummyRefCount : uint8_t { - kDisabled, - kEnabled, - }; - - enum class UseConfigurablePool : uint8_t { - kNo, - kIfAvailable, - }; - - // Constructor to suppress aggregate initialization. - constexpr PartitionOptions( - AlignedAlloc aligned_alloc, - ThreadCache thread_cache, - Quarantine quarantine, - Cookie cookie, - BackupRefPtr backup_ref_ptr, - BackupRefPtrZapping backup_ref_ptr_zapping, - UseConfigurablePool use_configurable_pool, - AddDummyRefCount add_dummy_ref_count = AddDummyRefCount::kDisabled -#if BUILDFLAG(ENABLE_PKEYS) - , - int pkey = internal::kDefaultPkey -#endif - ) - : aligned_alloc(aligned_alloc), - thread_cache(thread_cache), - quarantine(quarantine), - cookie(cookie), - backup_ref_ptr(backup_ref_ptr), - backup_ref_ptr_zapping(backup_ref_ptr_zapping), - use_configurable_pool(use_configurable_pool) -#if BUILDFLAG(ENABLE_PKEYS) - , - pkey(pkey) -#endif - { - } - - AlignedAlloc aligned_alloc; - ThreadCache thread_cache; - Quarantine quarantine; - Cookie cookie; - BackupRefPtr backup_ref_ptr; - BackupRefPtrZapping backup_ref_ptr_zapping; - UseConfigurablePool use_configurable_pool; - AddDummyRefCount add_dummy_ref_count = AddDummyRefCount::kDisabled; -#if BUILDFLAG(ENABLE_PKEYS) - int pkey; -#endif -}; - -// Never instantiate a PartitionRoot directly, instead use -// PartitionAllocator. -template <bool thread_safe> -struct PA_ALIGNAS(64) PA_COMPONENT_EXPORT(PARTITION_ALLOC) PartitionRoot { - using SlotSpan = internal::SlotSpanMetadata<thread_safe>; - using Page = internal::PartitionPage<thread_safe>; - using Bucket = internal::PartitionBucket<thread_safe>; - using FreeListEntry = internal::PartitionFreelistEntry; - using SuperPageExtentEntry = - internal::PartitionSuperPageExtentEntry<thread_safe>; - using DirectMapExtent = internal::PartitionDirectMapExtent<thread_safe>; -#if BUILDFLAG(USE_STARSCAN) - using PCScan = internal::PCScan; -#endif - - enum class QuarantineMode : uint8_t { - kAlwaysDisabled, - kDisabledByDefault, - kEnabled, - }; - - enum class ScanMode : uint8_t { - kDisabled, - kEnabled, - }; - - enum class BucketDistribution : uint8_t { kDefault, kDenser }; - - // Flags accessed on fast paths. - // - // Careful! PartitionAlloc's performance is sensitive to its layout. Please - // put the fast-path objects in the struct below, and the other ones after - // the union.. - struct Flags { - // Defines whether objects should be quarantined for this root. - QuarantineMode quarantine_mode; - - // Defines whether the root should be scanned. - ScanMode scan_mode; - - // It's important to default to the 'default' distribution, otherwise a - // switch from 'dense' -> 'default' would leave some buckets with dirty - // memory forever, since no memory would be allocated from these, their - // freelist would typically not be empty, making these unreclaimable. - BucketDistribution bucket_distribution = BucketDistribution::kDefault; - - bool with_thread_cache = false; - - bool allow_aligned_alloc; - bool allow_cookie; -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - bool brp_enabled_; - bool brp_zapping_enabled_; -#if PA_CONFIG(ENABLE_MAC11_MALLOC_SIZE_HACK) - bool mac11_malloc_size_hack_enabled_ = false; -#endif // PA_CONFIG(ENABLE_MAC11_MALLOC_SIZE_HACK) -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - bool use_configurable_pool; - -#if BUILDFLAG(ENABLE_PKEYS) - int pkey; -#endif - -#if PA_CONFIG(EXTRAS_REQUIRED) - uint32_t extras_size; - uint32_t extras_offset; -#else - // Teach the compiler that code can be optimized in builds that use no - // extras. - static inline constexpr uint32_t extras_size = 0; - static inline constexpr uint32_t extras_offset = 0; -#endif // PA_CONFIG(EXTRAS_REQUIRED) - }; - - // Read-mostly flags. - union { - Flags flags; - - // The flags above are accessed for all (de)allocations, and are mostly - // read-only. They should not share a cacheline with the data below, which - // is only touched when the lock is taken. - uint8_t one_cacheline[internal::kPartitionCachelineSize]; - }; - - // Not used on the fastest path (thread cache allocations), but on the fast - // path of the central allocator. - static_assert(thread_safe, "Only the thread-safe root is supported."); - ::partition_alloc::internal::Lock lock_; - - Bucket buckets[internal::kNumBuckets] = {}; - Bucket sentinel_bucket{}; - - // All fields below this comment are not accessed on the fast path. - bool initialized = false; - - // Bookkeeping. - // - total_size_of_super_pages - total virtual address space for normal bucket - // super pages - // - total_size_of_direct_mapped_pages - total virtual address space for - // direct-map regions - // - total_size_of_committed_pages - total committed pages for slots (doesn't - // include metadata, bitmaps (if any), or any data outside or regions - // described in #1 and #2) - // Invariant: total_size_of_allocated_bytes <= - // total_size_of_committed_pages < - // total_size_of_super_pages + - // total_size_of_direct_mapped_pages. - // Invariant: total_size_of_committed_pages <= max_size_of_committed_pages. - // Invariant: total_size_of_allocated_bytes <= max_size_of_allocated_bytes. - // Invariant: max_size_of_allocated_bytes <= max_size_of_committed_pages. - // Since all operations on the atomic variables have relaxed semantics, we - // don't check these invariants with DCHECKs. - std::atomic<size_t> total_size_of_committed_pages{0}; - std::atomic<size_t> max_size_of_committed_pages{0}; - std::atomic<size_t> total_size_of_super_pages{0}; - std::atomic<size_t> total_size_of_direct_mapped_pages{0}; - size_t total_size_of_allocated_bytes PA_GUARDED_BY(lock_) = 0; - size_t max_size_of_allocated_bytes PA_GUARDED_BY(lock_) = 0; - // Atomic, because system calls can be made without the lock held. - std::atomic<uint64_t> syscall_count{}; - std::atomic<uint64_t> syscall_total_time_ns{}; -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - std::atomic<size_t> total_size_of_brp_quarantined_bytes{0}; - std::atomic<size_t> total_count_of_brp_quarantined_slots{0}; - std::atomic<size_t> cumulative_size_of_brp_quarantined_bytes{0}; - std::atomic<size_t> cumulative_count_of_brp_quarantined_slots{0}; -#endif - // Slot span memory which has been provisioned, and is currently unused as - // it's part of an empty SlotSpan. This is not clean memory, since it has - // either been used for a memory allocation, and/or contains freelist - // entries. But it might have been moved to swap. Note that all this memory - // can be decommitted at any time. - size_t empty_slot_spans_dirty_bytes PA_GUARDED_BY(lock_) = 0; - - // Only tolerate up to |total_size_of_committed_pages >> - // max_empty_slot_spans_dirty_bytes_shift| dirty bytes in empty slot - // spans. That is, the default value of 3 tolerates up to 1/8. Since - // |empty_slot_spans_dirty_bytes| is never strictly larger than - // total_size_of_committed_pages, setting this to 0 removes the cap. This is - // useful to make tests deterministic and easier to reason about. - int max_empty_slot_spans_dirty_bytes_shift = 3; - - uintptr_t next_super_page = 0; - uintptr_t next_partition_page = 0; - uintptr_t next_partition_page_end = 0; - SuperPageExtentEntry* current_extent = nullptr; - SuperPageExtentEntry* first_extent = nullptr; - DirectMapExtent* direct_map_list PA_GUARDED_BY(lock_) = nullptr; - SlotSpan* - global_empty_slot_span_ring[internal::kMaxFreeableSpans] PA_GUARDED_BY( - lock_) = {}; - int16_t global_empty_slot_span_ring_index PA_GUARDED_BY(lock_) = 0; - int16_t global_empty_slot_span_ring_size PA_GUARDED_BY(lock_) = - internal::kDefaultEmptySlotSpanRingSize; - - // Integrity check = ~reinterpret_cast<uintptr_t>(this). - uintptr_t inverted_self = 0; - std::atomic<int> thread_caches_being_constructed_{0}; - - bool quarantine_always_for_testing = false; - - PartitionRoot() - : flags{QuarantineMode::kAlwaysDisabled, ScanMode::kDisabled} {} - explicit PartitionRoot(PartitionOptions opts) : flags() { Init(opts); } - // TODO(tasak): remove ~PartitionRoot() after confirming all tests - // don't need ~PartitionRoot(). - ~PartitionRoot(); - - // This will unreserve any space in the pool that the PartitionRoot is - // using. This is needed because many tests create and destroy many - // PartitionRoots over the lifetime of a process, which can exhaust the - // pool and cause tests to fail. - void DestructForTesting(); - -#if PA_CONFIG(ENABLE_MAC11_MALLOC_SIZE_HACK) - void EnableMac11MallocSizeHackForTesting(); -#endif // PA_CONFIG(ENABLE_MAC11_MALLOC_SIZE_HACK) - - // Public API - // - // Allocates out of the given bucket. Properly, this function should probably - // be in PartitionBucket, but because the implementation needs to be inlined - // for performance, and because it needs to inspect SlotSpanMetadata, - // it becomes impossible to have it in PartitionBucket as this causes a - // cyclical dependency on SlotSpanMetadata function implementations. - // - // Moving it a layer lower couples PartitionRoot and PartitionBucket, but - // preserves the layering of the includes. - void Init(PartitionOptions); - - void EnableThreadCacheIfSupported(); - - PA_ALWAYS_INLINE static bool IsValidSlotSpan(SlotSpan* slot_span); - PA_ALWAYS_INLINE static PartitionRoot* FromSlotSpan(SlotSpan* slot_span); - // These two functions work unconditionally for normal buckets. - // For direct map, they only work for the first super page of a reservation, - // (see partition_alloc_constants.h for the direct map allocation layout). - // In particular, the functions always work for a pointer to the start of a - // reservation. - PA_ALWAYS_INLINE static PartitionRoot* FromFirstSuperPage( - uintptr_t super_page); - PA_ALWAYS_INLINE static PartitionRoot* FromAddrInFirstSuperpage( - uintptr_t address); - - PA_ALWAYS_INLINE void DecreaseTotalSizeOfAllocatedBytes(uintptr_t addr, - size_t len) - PA_EXCLUSIVE_LOCKS_REQUIRED(lock_); - PA_ALWAYS_INLINE void IncreaseTotalSizeOfAllocatedBytes(uintptr_t addr, - size_t len, - size_t raw_size) - PA_EXCLUSIVE_LOCKS_REQUIRED(lock_); - PA_ALWAYS_INLINE void IncreaseCommittedPages(size_t len); - PA_ALWAYS_INLINE void DecreaseCommittedPages(size_t len); - PA_ALWAYS_INLINE void DecommitSystemPagesForData( - uintptr_t address, - size_t length, - PageAccessibilityDisposition accessibility_disposition) - PA_EXCLUSIVE_LOCKS_REQUIRED(lock_); - PA_ALWAYS_INLINE void RecommitSystemPagesForData( - uintptr_t address, - size_t length, - PageAccessibilityDisposition accessibility_disposition) - PA_EXCLUSIVE_LOCKS_REQUIRED(lock_); - PA_ALWAYS_INLINE bool TryRecommitSystemPagesForData( - uintptr_t address, - size_t length, - PageAccessibilityDisposition accessibility_disposition) - PA_LOCKS_EXCLUDED(lock_); - - [[noreturn]] PA_NOINLINE void OutOfMemory(size_t size); - - // Returns a pointer aligned on |alignment|, or nullptr. - // - // |alignment| has to be a power of two and a multiple of sizeof(void*) (as in - // posix_memalign() for POSIX systems). The returned pointer may include - // padding, and can be passed to |Free()| later. - // - // NOTE: This is incompatible with anything that adds extras before the - // returned pointer, such as ref-count. - PA_ALWAYS_INLINE void* AlignedAllocWithFlags(unsigned int flags, - size_t alignment, - size_t requested_size); - - // PartitionAlloc supports multiple partitions, and hence multiple callers to - // these functions. Setting PA_ALWAYS_INLINE bloats code, and can be - // detrimental to performance, for instance if multiple callers are hot (by - // increasing cache footprint). Set PA_NOINLINE on the "basic" top-level - // functions to mitigate that for "vanilla" callers. - PA_NOINLINE PA_MALLOC_FN void* Alloc(size_t requested_size, - const char* type_name) PA_MALLOC_ALIGNED; - PA_ALWAYS_INLINE PA_MALLOC_FN void* AllocWithFlags(unsigned int flags, - size_t requested_size, - const char* type_name) - PA_MALLOC_ALIGNED; - // Same as |AllocWithFlags()|, but allows specifying |slot_span_alignment|. It - // has to be a multiple of partition page size, greater than 0 and no greater - // than kMaxSupportedAlignment. If it equals exactly 1 partition page, no - // special action is taken as PartitoinAlloc naturally guarantees this - // alignment, otherwise a sub-optimial allocation strategy is used to - // guarantee the higher-order alignment. - PA_ALWAYS_INLINE PA_MALLOC_FN void* AllocWithFlagsInternal( - unsigned int flags, - size_t requested_size, - size_t slot_span_alignment, - const char* type_name) PA_MALLOC_ALIGNED; - // Same as |AllocWithFlags()|, but bypasses the allocator hooks. - // - // This is separate from AllocWithFlags() because other callers of - // AllocWithFlags() should not have the extra branch checking whether the - // hooks should be ignored or not. This is the same reason why |FreeNoHooks()| - // exists. However, |AlignedAlloc()| and |Realloc()| have few callers, so - // taking the extra branch in the non-malloc() case doesn't hurt. In addition, - // for the malloc() case, the compiler correctly removes the branch, since - // this is marked |PA_ALWAYS_INLINE|. - PA_ALWAYS_INLINE PA_MALLOC_FN void* AllocWithFlagsNoHooks( - unsigned int flags, - size_t requested_size, - size_t slot_span_alignment) PA_MALLOC_ALIGNED; - - PA_NOINLINE void* Realloc(void* ptr, - size_t newize, - const char* type_name) PA_MALLOC_ALIGNED; - // Overload that may return nullptr if reallocation isn't possible. In this - // case, |ptr| remains valid. - PA_NOINLINE void* TryRealloc(void* ptr, - size_t new_size, - const char* type_name) PA_MALLOC_ALIGNED; - PA_NOINLINE void* ReallocWithFlags(unsigned int flags, - void* ptr, - size_t new_size, - const char* type_name) PA_MALLOC_ALIGNED; - PA_NOINLINE static void Free(void* object); - PA_ALWAYS_INLINE static void FreeWithFlags(unsigned int flags, void* object); - // Same as |Free()|, bypasses the allocator hooks. - PA_ALWAYS_INLINE static void FreeNoHooks(void* object); - // Immediately frees the pointer bypassing the quarantine. |slot_start| is the - // beginning of the slot that contains |object|. - PA_ALWAYS_INLINE void FreeNoHooksImmediate(void* object, - SlotSpan* slot_span, - uintptr_t slot_start); - - PA_ALWAYS_INLINE static size_t GetUsableSize(void* ptr); - // Same as GetUsableSize() except it adjusts the return value for macOS 11 - // malloc_size() hack. - PA_ALWAYS_INLINE static size_t GetUsableSizeWithMac11MallocSizeHack( - void* ptr); - - PA_ALWAYS_INLINE PageAccessibilityConfiguration GetPageAccessibility() const; - PA_ALWAYS_INLINE PageAccessibilityConfiguration - PageAccessibilityWithPkeyIfEnabled( - PageAccessibilityConfiguration::Permissions) const; - - PA_ALWAYS_INLINE size_t - AllocationCapacityFromSlotStart(uintptr_t slot_start) const; - PA_ALWAYS_INLINE size_t - AllocationCapacityFromRequestedSize(size_t size) const; - - PA_ALWAYS_INLINE bool IsMemoryTaggingEnabled() const; - - // Frees memory from this partition, if possible, by decommitting pages or - // even entire slot spans. |flags| is an OR of base::PartitionPurgeFlags. - void PurgeMemory(int flags); - - // Reduces the size of the empty slot spans ring, until the dirty size is <= - // |limit|. - void ShrinkEmptySlotSpansRing(size_t limit) - PA_EXCLUSIVE_LOCKS_REQUIRED(lock_); - // The empty slot span ring starts "small", can be enlarged later. This - // improves performance by performing fewer system calls, at the cost of more - // memory usage. - void EnableLargeEmptySlotSpanRing() { - ::partition_alloc::internal::ScopedGuard locker{lock_}; - global_empty_slot_span_ring_size = internal::kMaxFreeableSpans; - } - - void DumpStats(const char* partition_name, - bool is_light_dump, - PartitionStatsDumper* partition_stats_dumper); - - static void DeleteForTesting(PartitionRoot* partition_root); - void ResetForTesting(bool allow_leaks); - void ResetBookkeepingForTesting(); - - PA_ALWAYS_INLINE BucketDistribution GetBucketDistribution() const { - return flags.bucket_distribution; - } - - static uint16_t SizeToBucketIndex(size_t size, - BucketDistribution bucket_distribution); - - PA_ALWAYS_INLINE void FreeInSlotSpan(uintptr_t slot_start, - SlotSpan* slot_span) - PA_EXCLUSIVE_LOCKS_REQUIRED(lock_); - - // Frees memory, with |slot_start| as returned by |RawAlloc()|. - PA_ALWAYS_INLINE void RawFree(uintptr_t slot_start); - PA_ALWAYS_INLINE void RawFree(uintptr_t slot_start, SlotSpan* slot_span) - PA_LOCKS_EXCLUDED(lock_); - - PA_ALWAYS_INLINE void RawFreeBatch(FreeListEntry* head, - FreeListEntry* tail, - size_t size, - SlotSpan* slot_span) - PA_LOCKS_EXCLUDED(lock_); - - PA_ALWAYS_INLINE void RawFreeWithThreadCache(uintptr_t slot_start, - SlotSpan* slot_span); - - // This is safe to do because we are switching to a bucket distribution with - // more buckets, meaning any allocations we have done before the switch are - // guaranteed to have a bucket under the new distribution when they are - // eventually deallocated. We do not need synchronization here. - void SwitchToDenserBucketDistribution() { - flags.bucket_distribution = BucketDistribution::kDenser; - } - // Switching back to the less dense bucket distribution is ok during tests. - // At worst, we end up with deallocations that are sent to a bucket that we - // cannot allocate from, which will not cause problems besides wasting - // memory. - void ResetBucketDistributionForTesting() { - flags.bucket_distribution = BucketDistribution::kDefault; - } - - ThreadCache* thread_cache_for_testing() const { - return flags.with_thread_cache ? ThreadCache::Get() : nullptr; - } - size_t get_total_size_of_committed_pages() const { - return total_size_of_committed_pages.load(std::memory_order_relaxed); - } - size_t get_max_size_of_committed_pages() const { - return max_size_of_committed_pages.load(std::memory_order_relaxed); - } - - size_t get_total_size_of_allocated_bytes() const { - // Since this is only used for bookkeeping, we don't care if the value is - // stale, so no need to get a lock here. - return PA_TS_UNCHECKED_READ(total_size_of_allocated_bytes); - } - - size_t get_max_size_of_allocated_bytes() const { - // Since this is only used for bookkeeping, we don't care if the value is - // stale, so no need to get a lock here. - return PA_TS_UNCHECKED_READ(max_size_of_allocated_bytes); - } - - internal::pool_handle ChoosePool() const { -#if BUILDFLAG(HAS_64_BIT_POINTERS) - if (flags.use_configurable_pool) { - PA_DCHECK(IsConfigurablePoolAvailable()); - return internal::kConfigurablePoolHandle; - } -#endif -#if BUILDFLAG(ENABLE_PKEYS) - if (flags.pkey != internal::kDefaultPkey) { - return internal::kPkeyPoolHandle; - } -#endif -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - return brp_enabled() ? internal::kBRPPoolHandle - : internal::kRegularPoolHandle; -#else - return internal::kRegularPoolHandle; -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - } - - PA_ALWAYS_INLINE bool IsQuarantineAllowed() const { - return flags.quarantine_mode != QuarantineMode::kAlwaysDisabled; - } - - PA_ALWAYS_INLINE bool IsQuarantineEnabled() const { - return flags.quarantine_mode == QuarantineMode::kEnabled; - } - - PA_ALWAYS_INLINE bool ShouldQuarantine(void* object) const { - if (PA_UNLIKELY(flags.quarantine_mode != QuarantineMode::kEnabled)) { - return false; - } -#if PA_CONFIG(HAS_MEMORY_TAGGING) - if (PA_UNLIKELY(quarantine_always_for_testing)) { - return true; - } - // If quarantine is enabled and the tag overflows, move the containing slot - // to quarantine, to prevent the attacker from exploiting a pointer that has - // an old tag. - if (PA_LIKELY(IsMemoryTaggingEnabled())) { - return internal::HasOverflowTag(object); - } - // Default behaviour if MTE is not enabled for this PartitionRoot. - return true; -#else - return true; -#endif - } - - PA_ALWAYS_INLINE void SetQuarantineAlwaysForTesting(bool value) { - quarantine_always_for_testing = value; - } - - PA_ALWAYS_INLINE bool IsScanEnabled() const { - // Enabled scan implies enabled quarantine. - PA_DCHECK(flags.scan_mode != ScanMode::kEnabled || IsQuarantineEnabled()); - return flags.scan_mode == ScanMode::kEnabled; - } - - PA_ALWAYS_INLINE static PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t - GetDirectMapMetadataAndGuardPagesSize() { - // Because we need to fake a direct-map region to look like a super page, we - // need to allocate more pages around the payload: - // - The first partition page is a combination of metadata and guard region. - // - We also add a trailing guard page. In most cases, a system page would - // suffice. But on 32-bit systems when BRP is on, we need a partition page - // to match granularity of the BRP pool bitmap. For cosistency, we'll use - // a partition page everywhere, which is cheap as it's uncommitted address - // space anyway. - return 2 * internal::PartitionPageSize(); - } - - PA_ALWAYS_INLINE static PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t - GetDirectMapSlotSize(size_t raw_size) { - // Caller must check that the size is not above the MaxDirectMapped() - // limit before calling. This also guards against integer overflow in the - // calculation here. - PA_DCHECK(raw_size <= internal::MaxDirectMapped()); - return partition_alloc::internal::base::bits::AlignUp( - raw_size, internal::SystemPageSize()); - } - - PA_ALWAYS_INLINE static size_t GetDirectMapReservationSize( - size_t padded_raw_size) { - // Caller must check that the size is not above the MaxDirectMapped() - // limit before calling. This also guards against integer overflow in the - // calculation here. - PA_DCHECK(padded_raw_size <= internal::MaxDirectMapped()); - return partition_alloc::internal::base::bits::AlignUp( - padded_raw_size + GetDirectMapMetadataAndGuardPagesSize(), - internal::DirectMapAllocationGranularity()); - } - - PA_ALWAYS_INLINE size_t AdjustSize0IfNeeded(size_t size) const { - // There are known cases where allowing size 0 would lead to problems: - // 1. If extras are present only before allocation (e.g. BRP ref-count), the - // extras will fill the entire kAlignment-sized slot, leading to - // returning a pointer to the next slot. ReallocWithFlags() calls - // SlotSpanMetadata::FromObject() prior to subtracting extras, thus - // potentially getting a wrong slot span. - // 2. If we put BRP ref-count in the previous slot, that slot may be free. - // In this case, the slot needs to fit both, a free-list entry and a - // ref-count. If sizeof(PartitionRefCount) is 8, it fills the entire - // smallest slot on 32-bit systems (kSmallestBucket is 8), thus not - // leaving space for the free-list entry. - // 3. On macOS and iOS, PartitionGetSizeEstimate() is used for two purposes: - // as a zone dispatcher and as an underlying implementation of - // malloc_size(3). As a zone dispatcher, zero has a special meaning of - // "doesn't belong to this zone". When extras fill out the entire slot, - // the usable size is 0, thus confusing the zone dispatcher. - // - // To save ourselves a branch on this hot path, we could eliminate this - // check at compile time for cases not listed above. The #if statement would - // be rather complex. Then there is also the fear of the unknown. The - // existing cases were discovered through obscure, painful-to-debug crashes. - // Better save ourselves trouble with not-yet-discovered cases. - if (PA_UNLIKELY(size == 0)) { - return 1; - } - return size; - } - - // Adjusts the size by adding extras. Also include the 0->1 adjustment if - // needed. - PA_ALWAYS_INLINE size_t AdjustSizeForExtrasAdd(size_t size) const { - size = AdjustSize0IfNeeded(size); - PA_DCHECK(size + flags.extras_size >= size); - return size + flags.extras_size; - } - - // Adjusts the size by subtracing extras. Doesn't include the 0->1 adjustment, - // which leads to an asymmetry with AdjustSizeForExtrasAdd, but callers of - // AdjustSizeForExtrasSubtract either expect the adjustment to be included, or - // are indifferent. - PA_ALWAYS_INLINE size_t AdjustSizeForExtrasSubtract(size_t size) const { - return size - flags.extras_size; - } - - PA_ALWAYS_INLINE uintptr_t SlotStartToObjectAddr(uintptr_t slot_start) const { - // TODO(bartekn): Check that |slot_start| is indeed a slot start. - return slot_start + flags.extras_offset; - } - - PA_ALWAYS_INLINE void* SlotStartToObject(uintptr_t slot_start) const { - // TODO(bartekn): Check that |slot_start| is indeed a slot start. - return internal::TagAddr(SlotStartToObjectAddr(slot_start)); - } - - PA_ALWAYS_INLINE uintptr_t ObjectToSlotStart(void* object) const { - return UntagPtr(object) - flags.extras_offset; - // TODO(bartekn): Check that the result is indeed a slot start. - } - - bool brp_enabled() const { -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - return flags.brp_enabled_; -#else - return false; -#endif - } - - bool brp_zapping_enabled() const { -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - return flags.brp_zapping_enabled_; -#else - return false; -#endif - } - - PA_ALWAYS_INLINE bool uses_configurable_pool() const { - return flags.use_configurable_pool; - } - - // To make tests deterministic, it is necessary to uncap the amount of memory - // waste incurred by empty slot spans. Otherwise, the size of various - // freelists, and committed memory becomes harder to reason about (and - // brittle) with a single thread, and non-deterministic with several. - void UncapEmptySlotSpanMemoryForTesting() { - max_empty_slot_spans_dirty_bytes_shift = 0; - } - - // Enables the sorting of active slot spans in PurgeMemory(). - static void EnableSortActiveSlotSpans(); - - private: - static inline bool sort_active_slot_spans_ = false; - - // |buckets| has `kNumBuckets` elements, but we sometimes access it at index - // `kNumBuckets`, which is occupied by the sentinel bucket. The correct layout - // is enforced by a static_assert() in partition_root.cc, so this is - // fine. However, UBSAN is correctly pointing out that there is an - // out-of-bounds access, so disable it for these accesses. - // - // See crbug.com/1150772 for an instance of Clusterfuzz / UBSAN detecting - // this. - PA_ALWAYS_INLINE const Bucket& PA_NO_SANITIZE("undefined") - bucket_at(size_t i) const { - PA_DCHECK(i <= internal::kNumBuckets); - return buckets[i]; - } - - // Returns whether a |bucket| from |this| root is direct-mapped. This function - // does not touch |bucket|, contrary to PartitionBucket::is_direct_mapped(). - // - // This is meant to be used in hot paths, and particularly *before* going into - // the thread cache fast path. Indeed, real-world profiles show that accessing - // an allocation's bucket is responsible for a sizable fraction of *total* - // deallocation time. This can be understood because - // - All deallocations have to access the bucket to know whether it is - // direct-mapped. If not (vast majority of allocations), it can go through - // the fast path, i.e. thread cache. - // - The bucket is relatively frequently written to, by *all* threads - // (e.g. every time a slot span becomes full or empty), so accessing it will - // result in some amount of cacheline ping-pong. - PA_ALWAYS_INLINE bool IsDirectMappedBucket(Bucket* bucket) const { - // All regular allocations are associated with a bucket in the |buckets_| - // array. A range check is then sufficient to identify direct-mapped - // allocations. - bool ret = !(bucket >= this->buckets && bucket <= &this->sentinel_bucket); - PA_DCHECK(ret == bucket->is_direct_mapped()); - return ret; - } - - // Allocates a memory slot, without initializing extras. - // - // - |flags| are as in AllocWithFlags(). - // - |raw_size| accommodates for extras on top of AllocWithFlags()'s - // |requested_size|. - // - |usable_size| and |is_already_zeroed| are output only. |usable_size| is - // guaranteed to be larger or equal to AllocWithFlags()'s |requested_size|. - PA_ALWAYS_INLINE uintptr_t RawAlloc(Bucket* bucket, - unsigned int flags, - size_t raw_size, - size_t slot_span_alignment, - size_t* usable_size, - bool* is_already_zeroed); - PA_ALWAYS_INLINE uintptr_t AllocFromBucket(Bucket* bucket, - unsigned int flags, - size_t raw_size, - size_t slot_span_alignment, - size_t* usable_size, - bool* is_already_zeroed) - PA_EXCLUSIVE_LOCKS_REQUIRED(lock_); - - bool TryReallocInPlaceForNormalBuckets(void* object, - SlotSpan* slot_span, - size_t new_size); - bool TryReallocInPlaceForDirectMap( - internal::SlotSpanMetadata<thread_safe>* slot_span, - size_t requested_size) PA_EXCLUSIVE_LOCKS_REQUIRED(lock_); - void DecommitEmptySlotSpans() PA_EXCLUSIVE_LOCKS_REQUIRED(lock_); - PA_ALWAYS_INLINE void RawFreeLocked(uintptr_t slot_start) - PA_EXCLUSIVE_LOCKS_REQUIRED(lock_); - ThreadCache* MaybeInitThreadCache(); - - // May return an invalid thread cache. - PA_ALWAYS_INLINE ThreadCache* GetOrCreateThreadCache(); - PA_ALWAYS_INLINE ThreadCache* GetThreadCache(); - -#if PA_CONFIG(USE_PARTITION_ROOT_ENUMERATOR) - static internal::Lock& GetEnumeratorLock(); - - PartitionRoot* PA_GUARDED_BY(GetEnumeratorLock()) next_root = nullptr; - PartitionRoot* PA_GUARDED_BY(GetEnumeratorLock()) prev_root = nullptr; - - friend class internal::PartitionRootEnumerator; -#endif // PA_CONFIG(USE_PARTITION_ROOT_ENUMERATOR) - - friend class ThreadCache; -}; - -namespace internal { - -class ScopedSyscallTimer { - public: -#if PA_CONFIG(COUNT_SYSCALL_TIME) - explicit ScopedSyscallTimer(PartitionRoot<>* root) - : root_(root), tick_(base::TimeTicks::Now()) {} - - ~ScopedSyscallTimer() { - root_->syscall_count.fetch_add(1, std::memory_order_relaxed); - - int64_t elapsed_nanos = (base::TimeTicks::Now() - tick_).InNanoseconds(); - if (elapsed_nanos > 0) { - root_->syscall_total_time_ns.fetch_add( - static_cast<uint64_t>(elapsed_nanos), std::memory_order_relaxed); - } - } - - private: - PartitionRoot<>* root_; - const base::TimeTicks tick_; -#else - explicit ScopedSyscallTimer(PartitionRoot<>* root) { - root->syscall_count.fetch_add(1, std::memory_order_relaxed); - } -#endif -}; - -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - -PA_ALWAYS_INLINE uintptr_t -PartitionAllocGetDirectMapSlotStartInBRPPool(uintptr_t address) { - PA_DCHECK(IsManagedByPartitionAllocBRPPool(address)); -#if BUILDFLAG(HAS_64_BIT_POINTERS) - // Use this variant of GetDirectMapReservationStart as it has better - // performance. - uintptr_t offset = OffsetInBRPPool(address); - uintptr_t reservation_start = - GetDirectMapReservationStart(address, kBRPPoolHandle, offset); -#else // BUILDFLAG(HAS_64_BIT_POINTERS) - uintptr_t reservation_start = GetDirectMapReservationStart(address); -#endif - if (!reservation_start) { - return 0; - } - - // The direct map allocation may not start exactly from the first page, as - // there may be padding for alignment. The first page metadata holds an offset - // to where direct map metadata, and thus direct map start, are located. - auto* first_page = PartitionPage<ThreadSafe>::FromAddr(reservation_start + - PartitionPageSize()); - auto* page = first_page + first_page->slot_span_metadata_offset; - PA_DCHECK(page->is_valid); - PA_DCHECK(!page->slot_span_metadata_offset); - auto* slot_span = &page->slot_span_metadata; - uintptr_t slot_start = - SlotSpanMetadata<ThreadSafe>::ToSlotSpanStart(slot_span); -#if BUILDFLAG(PA_DCHECK_IS_ON) - auto* metadata = - PartitionDirectMapMetadata<ThreadSafe>::FromSlotSpan(slot_span); - size_t padding_for_alignment = - metadata->direct_map_extent.padding_for_alignment; - PA_DCHECK(padding_for_alignment == - static_cast<size_t>(page - first_page) * PartitionPageSize()); - PA_DCHECK(slot_start == - reservation_start + PartitionPageSize() + padding_for_alignment); -#endif // BUILDFLAG(PA_DCHECK_IS_ON) - return slot_start; -} - -// Gets the address to the beginning of the allocated slot. The input |address| -// can point anywhere in the slot, including the slot start as well as -// immediately past the slot. -// -// This isn't a general purpose function, it is used specifically for obtaining -// BackupRefPtr's ref-count. The caller is responsible for ensuring that the -// ref-count is in place for this allocation. -PA_ALWAYS_INLINE uintptr_t -PartitionAllocGetSlotStartInBRPPool(uintptr_t address) { - // Adjust to support pointers right past the end of an allocation, which in - // some cases appear to point outside the designated allocation slot. - // - // If ref-count is present before the allocation, then adjusting a valid - // pointer down will not cause us to go down to the previous slot, otherwise - // no adjustment is needed (and likely wouldn't be correct as there is - // a risk of going down to the previous slot). Either way, - // kPartitionPastAllocationAdjustment takes care of that detail. - address -= kPartitionPastAllocationAdjustment; - PA_DCHECK(IsManagedByNormalBucketsOrDirectMap(address)); - DCheckIfManagedByPartitionAllocBRPPool(address); - - uintptr_t directmap_slot_start = - PartitionAllocGetDirectMapSlotStartInBRPPool(address); - if (PA_UNLIKELY(directmap_slot_start)) { - return directmap_slot_start; - } - auto* slot_span = SlotSpanMetadata<ThreadSafe>::FromAddr(address); - auto* root = PartitionRoot<ThreadSafe>::FromSlotSpan(slot_span); - // Double check that ref-count is indeed present. - PA_DCHECK(root->brp_enabled()); - - // Get the offset from the beginning of the slot span. - uintptr_t slot_span_start = - SlotSpanMetadata<ThreadSafe>::ToSlotSpanStart(slot_span); - size_t offset_in_slot_span = address - slot_span_start; - - auto* bucket = slot_span->bucket; - return slot_span_start + - bucket->slot_size * bucket->GetSlotNumber(offset_in_slot_span); -} - -// Return values to indicate where a pointer is pointing relative to the bounds -// of an allocation. -enum class PtrPosWithinAlloc { - // When BACKUP_REF_PTR_POISON_OOB_PTR is disabled, end-of-allocation pointers - // are also considered in-bounds. - kInBounds, -#if BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR) - kAllocEnd, -#endif - kFarOOB -}; - -// Checks whether `test_address` is in the same allocation slot as -// `orig_address`. -// -// This can be called after adding or subtracting from the `orig_address` -// to produce a different pointer which must still stay in the same allocation. -// -// The `type_size` is the size of the type that the raw_ptr is pointing to, -// which may be the type the allocation is holding or a compatible pointer type -// such as a base class or char*. It is used to detect pointers near the end of -// the allocation but not strictly beyond it. -// -// This isn't a general purpose function. The caller is responsible for ensuring -// that the ref-count is in place for this allocation. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -PtrPosWithinAlloc IsPtrWithinSameAlloc(uintptr_t orig_address, - uintptr_t test_address, - size_t type_size); - -PA_ALWAYS_INLINE void PartitionAllocFreeForRefCounting(uintptr_t slot_start) { - PA_DCHECK(!PartitionRefCountPointer(slot_start)->IsAlive()); - - auto* slot_span = SlotSpanMetadata<ThreadSafe>::FromSlotStart(slot_start); - auto* root = PartitionRoot<ThreadSafe>::FromSlotSpan(slot_span); - // PartitionRefCount is required to be allocated inside a `PartitionRoot` that - // supports reference counts. - PA_DCHECK(root->brp_enabled()); - - // Iterating over the entire slot can be really expensive. -#if BUILDFLAG(PA_EXPENSIVE_DCHECKS_ARE_ON) - auto hook = PartitionAllocHooks::GetQuarantineOverrideHook(); - // If we have a hook the object segment is not necessarily filled - // with |kQuarantinedByte|. - if (PA_LIKELY(!hook)) { - unsigned char* object = - static_cast<unsigned char*>(root->SlotStartToObject(slot_start)); - for (size_t i = 0; i < slot_span->GetUsableSize(root); ++i) { - PA_DCHECK(object[i] == kQuarantinedByte); - } - } - DebugMemset(SlotStartAddr2Ptr(slot_start), kFreedByte, - slot_span->GetUtilizedSlotSize() -#if BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) - - sizeof(PartitionRefCount) -#endif - ); -#endif - - root->total_size_of_brp_quarantined_bytes.fetch_sub( - slot_span->GetSlotSizeForBookkeeping(), std::memory_order_relaxed); - root->total_count_of_brp_quarantined_slots.fetch_sub( - 1, std::memory_order_relaxed); - - root->RawFreeWithThreadCache(slot_start, slot_span); -} -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - -} // namespace internal - -template <bool thread_safe> -PA_ALWAYS_INLINE uintptr_t -PartitionRoot<thread_safe>::AllocFromBucket(Bucket* bucket, - unsigned int flags, - size_t raw_size, - size_t slot_span_alignment, - size_t* usable_size, - bool* is_already_zeroed) { - PA_DCHECK((slot_span_alignment >= internal::PartitionPageSize()) && - internal::base::bits::IsPowerOfTwo(slot_span_alignment)); - SlotSpan* slot_span = bucket->active_slot_spans_head; - // There always must be a slot span on the active list (could be a sentinel). - PA_DCHECK(slot_span); - // Check that it isn't marked full, which could only be true if the span was - // removed from the active list. - PA_DCHECK(!slot_span->marked_full); - - uintptr_t slot_start = - internal::SlotStartPtr2Addr(slot_span->get_freelist_head()); - // Use the fast path when a slot is readily available on the free list of the - // first active slot span. However, fall back to the slow path if a - // higher-order alignment is requested, because an inner slot of an existing - // slot span is unlikely to satisfy it. - if (PA_LIKELY(slot_span_alignment <= internal::PartitionPageSize() && - slot_start)) { - *is_already_zeroed = false; - // This is a fast path, avoid calling GetUsableSize() in Release builds - // as it is costlier. Copy its small bucket path instead. - *usable_size = AdjustSizeForExtrasSubtract(bucket->slot_size); - PA_DCHECK(*usable_size == slot_span->GetUsableSize(this)); - - // If these DCHECKs fire, you probably corrupted memory. - // TODO(crbug.com/1257655): See if we can afford to make these CHECKs. - PA_DCHECK(IsValidSlotSpan(slot_span)); - - // All large allocations must go through the slow path to correctly update - // the size metadata. - PA_DCHECK(!slot_span->CanStoreRawSize()); - PA_DCHECK(!slot_span->bucket->is_direct_mapped()); - void* entry = slot_span->PopForAlloc(bucket->slot_size); - PA_DCHECK(internal::SlotStartPtr2Addr(entry) == slot_start); - - PA_DCHECK(slot_span->bucket == bucket); - } else { - slot_start = bucket->SlowPathAlloc(this, flags, raw_size, - slot_span_alignment, is_already_zeroed); - if (PA_UNLIKELY(!slot_start)) { - return 0; - } - - slot_span = SlotSpan::FromSlotStart(slot_start); - // TODO(crbug.com/1257655): See if we can afford to make this a CHECK. - PA_DCHECK(IsValidSlotSpan(slot_span)); - // For direct mapped allocations, |bucket| is the sentinel. - PA_DCHECK((slot_span->bucket == bucket) || - (slot_span->bucket->is_direct_mapped() && - (bucket == &sentinel_bucket))); - - *usable_size = slot_span->GetUsableSize(this); - } - PA_DCHECK(slot_span->GetUtilizedSlotSize() <= slot_span->bucket->slot_size); - IncreaseTotalSizeOfAllocatedBytes( - slot_start, slot_span->GetSlotSizeForBookkeeping(), raw_size); - -#if BUILDFLAG(USE_FREESLOT_BITMAP) - if (!slot_span->bucket->is_direct_mapped()) { - internal::FreeSlotBitmapMarkSlotAsUsed(slot_start); - } -#endif - - return slot_start; -} - -// static -template <bool thread_safe> -PA_NOINLINE void PartitionRoot<thread_safe>::Free(void* object) { - return FreeWithFlags(0, object); -} - -// static -template <bool thread_safe> -PA_ALWAYS_INLINE void PartitionRoot<thread_safe>::FreeWithFlags( - unsigned int flags, - void* object) { - PA_DCHECK(flags < FreeFlags::kLastFlag << 1); - -#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - if (!(flags & FreeFlags::kNoMemoryToolOverride)) { - free(object); - return; - } -#endif // defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - if (PA_UNLIKELY(!object)) { - return; - } - - if (PartitionAllocHooks::AreHooksEnabled()) { - PartitionAllocHooks::FreeObserverHookIfEnabled(object); - if (PartitionAllocHooks::FreeOverrideHookIfEnabled(object)) { - return; - } - } - - FreeNoHooks(object); -} - -// Returns whether MTE is supported for this partition root. Because MTE stores -// tagging information in the high bits of the pointer, it causes issues with -// components like V8's ArrayBuffers which use custom pointer representations. -// All custom representations encountered so far rely on an "is in configurable -// pool?" check, so we use that as a proxy. -template <bool thread_safe> -PA_ALWAYS_INLINE bool PartitionRoot<thread_safe>::IsMemoryTaggingEnabled() - const { -#if PA_CONFIG(HAS_MEMORY_TAGGING) - return !flags.use_configurable_pool; -#else - return false; -#endif -} - -// static -template <bool thread_safe> -PA_ALWAYS_INLINE void PartitionRoot<thread_safe>::FreeNoHooks(void* object) { - if (PA_UNLIKELY(!object)) { - return; - } - // Almost all calls to FreeNoNooks() will end up writing to |*object|, the - // only cases where we don't would be delayed free() in PCScan, but |*object| - // can be cold in cache. - PA_PREFETCH(object); - uintptr_t object_addr = internal::ObjectPtr2Addr(object); - - // On Android, malloc() interception is more fragile than on other - // platforms, as we use wrapped symbols. However, the pools allow us to - // quickly tell that a pointer was allocated with PartitionAlloc. - // - // This is a crash to detect imperfect symbol interception. However, we can - // forward allocations we don't own to the system malloc() implementation in - // these rare cases, assuming that some remain. - // - // On Android Chromecast devices, this is already checked in PartitionFree() - // in the shim. -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \ - (BUILDFLAG(IS_ANDROID) && !BUILDFLAG(PA_IS_CAST_ANDROID)) - PA_CHECK(IsManagedByPartitionAlloc(object_addr)); -#endif - - // Fetch the root from the address, and not SlotSpanMetadata. This is - // important, as obtaining it from SlotSpanMetadata is a slow operation - // (looking into the metadata area, and following a pointer), which can induce - // cache coherency traffic (since they're read on every free(), and written to - // on any malloc()/free() that is not a hit in the thread cache). This way we - // change the critical path from object -> slot_span -> root into two - // *parallel* ones: - // 1. object -> root - // 2. object -> slot_span - auto* root = FromAddrInFirstSuperpage(object_addr); - SlotSpan* slot_span = SlotSpan::FromObject(object); - PA_DCHECK(FromSlotSpan(slot_span) == root); - - uintptr_t slot_start = root->ObjectToSlotStart(object); - PA_DCHECK(slot_span == SlotSpan::FromSlotStart(slot_start)); - -#if PA_CONFIG(HAS_MEMORY_TAGGING) - if (PA_LIKELY(root->IsMemoryTaggingEnabled())) { - const size_t slot_size = slot_span->bucket->slot_size; - if (PA_LIKELY(slot_size <= internal::kMaxMemoryTaggingSize)) { - // slot_span is untagged at this point, so we have to recover its tag - // again to increment and provide use-after-free mitigations. - internal::TagMemoryRangeIncrement(internal::TagAddr(slot_start), - slot_size); - // Incrementing the MTE-tag in the memory range invalidates the |object|'s - // tag, so it must be retagged. - object = internal::TagPtr(object); - } - } -#else - // We are going to read from |*slot_span| in all branches, but haven't done it - // yet. - // - // TODO(crbug.com/1207307): It would be much better to avoid touching - // |*slot_span| at all on the fast path, or at least to separate its read-only - // parts (i.e. bucket pointer) from the rest. Indeed, every thread cache miss - // (or batch fill) will *write* to |slot_span->freelist_head|, leading to - // cacheline ping-pong. - // - // Don't do it when memory tagging is enabled, as |*slot_span| has already - // been touched above. - PA_PREFETCH(slot_span); -#endif // PA_CONFIG(HAS_MEMORY_TAGGING) - -#if BUILDFLAG(USE_STARSCAN) - // TODO(bikineev): Change the condition to PA_LIKELY once PCScan is enabled by - // default. - if (PA_UNLIKELY(root->ShouldQuarantine(object))) { - // PCScan safepoint. Call before potentially scheduling scanning task. - PCScan::JoinScanIfNeeded(); - if (PA_LIKELY(internal::IsManagedByNormalBuckets(slot_start))) { - PCScan::MoveToQuarantine(object, slot_span->GetUsableSize(root), - slot_start, slot_span->bucket->slot_size); - return; - } - } -#endif // BUILDFLAG(USE_STARSCAN) - - root->FreeNoHooksImmediate(object, slot_span, slot_start); -} - -template <bool thread_safe> -PA_ALWAYS_INLINE void PartitionRoot<thread_safe>::FreeNoHooksImmediate( - void* object, - SlotSpan* slot_span, - uintptr_t slot_start) { - // The thread cache is added "in the middle" of the main allocator, that is: - // - After all the cookie/ref-count management - // - Before the "raw" allocator. - // - // On the deallocation side: - // 1. Check cookie/ref-count, adjust the pointer - // 2. Deallocation - // a. Return to the thread cache if possible. If it succeeds, return. - // b. Otherwise, call the "raw" allocator <-- Locking - PA_DCHECK(object); - PA_DCHECK(slot_span); - PA_DCHECK(IsValidSlotSpan(slot_span)); - PA_DCHECK(slot_start); - - // Layout inside the slot: - // |[refcnt]|...object...|[empty]|[cookie]|[unused]| - // <--------(a)---------> - // <--(b)---> + <--(b)---> - // <-----------------(c)------------------> - // (a) usable_size - // (b) extras - // (c) utilized_slot_size - // - // If PUT_REF_COUNT_IN_PREVIOUS_SLOT is set, the layout is: - // |...object...|[empty]|[cookie]|[unused]|[refcnt]| - // <--------(a)---------> - // <--(b)---> + <--(b)---> - // <-------------(c)-------------> + <--(c)---> - // - // Note: ref-count and cookie can be 0-sized. - // - // For more context, see the other "Layout inside the slot" comment inside - // AllocWithFlagsNoHooks(). - -#if BUILDFLAG(PA_DCHECK_IS_ON) - if (flags.allow_cookie) { - // Verify the cookie after the allocated region. - // If this assert fires, you probably corrupted memory. - internal::PartitionCookieCheckValue(static_cast<unsigned char*>(object) + - slot_span->GetUsableSize(this)); - } -#endif - -#if BUILDFLAG(USE_STARSCAN) - // TODO(bikineev): Change the condition to PA_LIKELY once PCScan is enabled by - // default. - if (PA_UNLIKELY(IsQuarantineEnabled())) { - if (PA_LIKELY(internal::IsManagedByNormalBuckets(slot_start))) { - // Mark the state in the state bitmap as freed. - internal::StateBitmapFromAddr(slot_start)->Free(slot_start); - } - } -#endif // BUILDFLAG(USE_STARSCAN) - -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - // TODO(keishi): Add PA_LIKELY when brp is fully enabled as |brp_enabled| will - // be false only for the aligned partition. - if (brp_enabled()) { - auto* ref_count = internal::PartitionRefCountPointer(slot_start); - // If there are no more references to the allocation, it can be freed - // immediately. Otherwise, defer the operation and zap the memory to turn - // potential use-after-free issues into unexploitable crashes. - if (PA_UNLIKELY(!ref_count->IsAliveWithNoKnownRefs() && - brp_zapping_enabled())) { - auto usable_size = slot_span->GetUsableSize(this); - auto hook = PartitionAllocHooks::GetQuarantineOverrideHook(); - if (PA_UNLIKELY(hook)) { - hook(object, usable_size); - } else { - internal::SecureMemset(object, internal::kQuarantinedByte, usable_size); - } - } - - if (PA_UNLIKELY(!(ref_count->ReleaseFromAllocator()))) { - total_size_of_brp_quarantined_bytes.fetch_add( - slot_span->GetSlotSizeForBookkeeping(), std::memory_order_relaxed); - total_count_of_brp_quarantined_slots.fetch_add(1, - std::memory_order_relaxed); - cumulative_size_of_brp_quarantined_bytes.fetch_add( - slot_span->GetSlotSizeForBookkeeping(), std::memory_order_relaxed); - cumulative_count_of_brp_quarantined_slots.fetch_add( - 1, std::memory_order_relaxed); - return; - } - } -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - - // memset() can be really expensive. -#if BUILDFLAG(PA_EXPENSIVE_DCHECKS_ARE_ON) - internal::DebugMemset(internal::SlotStartAddr2Ptr(slot_start), - internal::kFreedByte, - slot_span->GetUtilizedSlotSize() -#if BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) - - sizeof(internal::PartitionRefCount) -#endif - ); -#elif PA_CONFIG(ZERO_RANDOMLY_ON_FREE) - // `memset` only once in a while: we're trading off safety for time - // efficiency. - if (PA_UNLIKELY(internal::RandomPeriod()) && - !IsDirectMappedBucket(slot_span->bucket)) { - internal::SecureMemset(internal::SlotStartAddr2Ptr(slot_start), 0, - slot_span->GetUtilizedSlotSize() -#if BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) - - sizeof(internal::PartitionRefCount) -#endif - ); - } -#endif // PA_CONFIG(ZERO_RANDOMLY_ON_FREE) - - RawFreeWithThreadCache(slot_start, slot_span); -} - -template <bool thread_safe> -PA_ALWAYS_INLINE void PartitionRoot<thread_safe>::FreeInSlotSpan( - uintptr_t slot_start, - SlotSpan* slot_span) { - DecreaseTotalSizeOfAllocatedBytes(slot_start, - slot_span->GetSlotSizeForBookkeeping()); - -#if BUILDFLAG(USE_FREESLOT_BITMAP) - if (!slot_span->bucket->is_direct_mapped()) { - internal::FreeSlotBitmapMarkSlotAsFree(slot_start); - } -#endif - - return slot_span->Free(slot_start); -} - -template <bool thread_safe> -PA_ALWAYS_INLINE void PartitionRoot<thread_safe>::RawFree( - uintptr_t slot_start) { - SlotSpan* slot_span = SlotSpan::FromSlotStart(slot_start); - RawFree(slot_start, slot_span); -} - -#if PA_CONFIG(IS_NONCLANG_MSVC) -// MSVC only supports inline assembly on x86. This preprocessor directive -// is intended to be a replacement for the same. -// -// TODO(crbug.com/1351310): Make sure inlining doesn't degrade this into -// a no-op or similar. The documentation doesn't say. -#pragma optimize("", off) -#endif -template <bool thread_safe> -PA_ALWAYS_INLINE void PartitionRoot<thread_safe>::RawFree(uintptr_t slot_start, - SlotSpan* slot_span) { - // At this point we are about to acquire the lock, so we try to minimize the - // risk of blocking inside the locked section. - // - // For allocations that are not direct-mapped, there will always be a store at - // the beginning of |*slot_start|, to link the freelist. This is why there is - // a prefetch of it at the beginning of the free() path. - // - // However, the memory which is being freed can be very cold (for instance - // during browser shutdown, when various caches are finally completely freed), - // and so moved to either compressed memory or swap. This means that touching - // it here can cause a major page fault. This is in turn will cause - // descheduling of the thread *while locked*. Since we don't have priority - // inheritance locks on most platforms, avoiding long locked periods relies on - // the OS having proper priority boosting. There is evidence - // (crbug.com/1228523) that this is not always the case on Windows, and a very - // low priority background thread can block the main one for a long time, - // leading to hangs. - // - // To mitigate that, make sure that we fault *before* locking. Note that this - // is useless for direct-mapped allocations (which are very rare anyway), and - // that this path is *not* taken for thread cache bucket purge (since it calls - // RawFreeLocked()). This is intentional, as the thread cache is purged often, - // and the memory has a consequence the memory has already been touched - // recently (to link the thread cache freelist). - *static_cast<volatile uintptr_t*>(internal::SlotStartAddr2Ptr(slot_start)) = - 0; - // Note: even though we write to slot_start + sizeof(void*) as well, due to - // alignment constraints, the two locations are always going to be in the same - // OS page. No need to write to the second one as well. - // - // Do not move the store above inside the locked section. -#if !(PA_CONFIG(IS_NONCLANG_MSVC)) - __asm__ __volatile__("" : : "r"(slot_start) : "memory"); -#endif - - ::partition_alloc::internal::ScopedGuard guard{lock_}; - FreeInSlotSpan(slot_start, slot_span); -} -#if PA_CONFIG(IS_NONCLANG_MSVC) -#pragma optimize("", on) -#endif - -template <bool thread_safe> -PA_ALWAYS_INLINE void PartitionRoot<thread_safe>::RawFreeBatch( - FreeListEntry* head, - FreeListEntry* tail, - size_t size, - SlotSpan* slot_span) { - PA_DCHECK(head); - PA_DCHECK(tail); - PA_DCHECK(size > 0); - PA_DCHECK(slot_span); - PA_DCHECK(IsValidSlotSpan(slot_span)); - // The passed freelist is likely to be just built up, which means that the - // corresponding pages were faulted in (without acquiring the lock). So there - // is no need to touch pages manually here before the lock. - ::partition_alloc::internal::ScopedGuard guard{lock_}; - // TODO(thiabaud): Fix the accounting here. The size is correct, but the - // pointer is not. This only affects local tools that record each allocation, - // not our metrics. - DecreaseTotalSizeOfAllocatedBytes( - 0u, slot_span->GetSlotSizeForBookkeeping() * size); - slot_span->AppendFreeList(head, tail, size); -} - -template <bool thread_safe> -PA_ALWAYS_INLINE void PartitionRoot<thread_safe>::RawFreeWithThreadCache( - uintptr_t slot_start, - SlotSpan* slot_span) { - // PA_LIKELY: performance-sensitive partitions have a thread cache, - // direct-mapped allocations are uncommon. - ThreadCache* thread_cache = GetThreadCache(); - if (PA_LIKELY(ThreadCache::IsValid(thread_cache) && - !IsDirectMappedBucket(slot_span->bucket))) { - size_t bucket_index = - static_cast<size_t>(slot_span->bucket - this->buckets); - size_t slot_size; - if (PA_LIKELY(thread_cache->MaybePutInCache(slot_start, bucket_index, - &slot_size))) { - // This is a fast path, avoid calling GetUsableSize() in Release builds - // as it is costlier. Copy its small bucket path instead. - PA_DCHECK(!slot_span->CanStoreRawSize()); - size_t usable_size = AdjustSizeForExtrasSubtract(slot_size); - PA_DCHECK(usable_size == slot_span->GetUsableSize(this)); - thread_cache->RecordDeallocation(usable_size); - return; - } - } - - if (PA_LIKELY(ThreadCache::IsValid(thread_cache))) { - // Accounting must be done outside `RawFree()`, as it's also called from the - // thread cache. We would double-count otherwise. - // - // GetUsableSize() will always give the correct result, and we are in a slow - // path here (since the thread cache case returned earlier). - size_t usable_size = slot_span->GetUsableSize(this); - thread_cache->RecordDeallocation(usable_size); - } - RawFree(slot_start, slot_span); -} - -template <bool thread_safe> -PA_ALWAYS_INLINE void PartitionRoot<thread_safe>::RawFreeLocked( - uintptr_t slot_start) { - SlotSpan* slot_span = SlotSpan::FromSlotStart(slot_start); - // Direct-mapped deallocation releases then re-acquires the lock. The caller - // may not expect that, but we never call this function on direct-mapped - // allocations. - PA_DCHECK(!IsDirectMappedBucket(slot_span->bucket)); - FreeInSlotSpan(slot_start, slot_span); -} - -// static -template <bool thread_safe> -PA_ALWAYS_INLINE bool PartitionRoot<thread_safe>::IsValidSlotSpan( - SlotSpan* slot_span) { - PartitionRoot* root = FromSlotSpan(slot_span); - return root->inverted_self == ~reinterpret_cast<uintptr_t>(root); -} - -template <bool thread_safe> -PA_ALWAYS_INLINE PartitionRoot<thread_safe>* -PartitionRoot<thread_safe>::FromSlotSpan(SlotSpan* slot_span) { - auto* extent_entry = reinterpret_cast<SuperPageExtentEntry*>( - reinterpret_cast<uintptr_t>(slot_span) & internal::SystemPageBaseMask()); - return extent_entry->root; -} - -template <bool thread_safe> -PA_ALWAYS_INLINE PartitionRoot<thread_safe>* -PartitionRoot<thread_safe>::FromFirstSuperPage(uintptr_t super_page) { - PA_DCHECK(internal::IsReservationStart(super_page)); - auto* extent_entry = - internal::PartitionSuperPageToExtent<thread_safe>(super_page); - PartitionRoot* root = extent_entry->root; - PA_DCHECK(root->inverted_self == ~reinterpret_cast<uintptr_t>(root)); - return root; -} - -template <bool thread_safe> -PA_ALWAYS_INLINE PartitionRoot<thread_safe>* -PartitionRoot<thread_safe>::FromAddrInFirstSuperpage(uintptr_t address) { - uintptr_t super_page = address & internal::kSuperPageBaseMask; - PA_DCHECK(internal::IsReservationStart(super_page)); - return FromFirstSuperPage(super_page); -} - -template <bool thread_safe> -PA_ALWAYS_INLINE void -PartitionRoot<thread_safe>::IncreaseTotalSizeOfAllocatedBytes(uintptr_t addr, - size_t len, - size_t raw_size) { - total_size_of_allocated_bytes += len; - max_size_of_allocated_bytes = - std::max(max_size_of_allocated_bytes, total_size_of_allocated_bytes); -#if BUILDFLAG(RECORD_ALLOC_INFO) - partition_alloc::internal::RecordAllocOrFree(addr | 0x01, raw_size); -#endif // BUILDFLAG(RECORD_ALLOC_INFO) -} - -template <bool thread_safe> -PA_ALWAYS_INLINE void -PartitionRoot<thread_safe>::DecreaseTotalSizeOfAllocatedBytes(uintptr_t addr, - size_t len) { - // An underflow here means we've miscounted |total_size_of_allocated_bytes| - // somewhere. - PA_DCHECK(total_size_of_allocated_bytes >= len); - total_size_of_allocated_bytes -= len; -#if BUILDFLAG(RECORD_ALLOC_INFO) - partition_alloc::internal::RecordAllocOrFree(addr | 0x00, len); -#endif // BUILDFLAG(RECORD_ALLOC_INFO) -} - -template <bool thread_safe> -PA_ALWAYS_INLINE void PartitionRoot<thread_safe>::IncreaseCommittedPages( - size_t len) { - const auto old_total = - total_size_of_committed_pages.fetch_add(len, std::memory_order_relaxed); - - const auto new_total = old_total + len; - - // This function is called quite frequently; to avoid performance problems, we - // don't want to hold a lock here, so we use compare and exchange instead. - size_t expected = max_size_of_committed_pages.load(std::memory_order_relaxed); - size_t desired; - do { - desired = std::max(expected, new_total); - } while (!max_size_of_committed_pages.compare_exchange_weak( - expected, desired, std::memory_order_relaxed, std::memory_order_relaxed)); -} - -template <bool thread_safe> -PA_ALWAYS_INLINE void PartitionRoot<thread_safe>::DecreaseCommittedPages( - size_t len) { - total_size_of_committed_pages.fetch_sub(len, std::memory_order_relaxed); -} - -template <bool thread_safe> -PA_ALWAYS_INLINE void PartitionRoot<thread_safe>::DecommitSystemPagesForData( - uintptr_t address, - size_t length, - PageAccessibilityDisposition accessibility_disposition) { - internal::ScopedSyscallTimer timer{this}; - DecommitSystemPages(address, length, accessibility_disposition); - DecreaseCommittedPages(length); -} - -// Not unified with TryRecommitSystemPagesForData() to preserve error codes. -template <bool thread_safe> -PA_ALWAYS_INLINE void PartitionRoot<thread_safe>::RecommitSystemPagesForData( - uintptr_t address, - size_t length, - PageAccessibilityDisposition accessibility_disposition) { - internal::ScopedSyscallTimer timer{this}; - - bool ok = TryRecommitSystemPages(address, length, GetPageAccessibility(), - accessibility_disposition); - if (PA_UNLIKELY(!ok)) { - // Decommit some memory and retry. The alternative is crashing. - DecommitEmptySlotSpans(); - RecommitSystemPages(address, length, GetPageAccessibility(), - accessibility_disposition); - } - - IncreaseCommittedPages(length); -} - -template <bool thread_safe> -PA_ALWAYS_INLINE bool PartitionRoot<thread_safe>::TryRecommitSystemPagesForData( - uintptr_t address, - size_t length, - PageAccessibilityDisposition accessibility_disposition) { - internal::ScopedSyscallTimer timer{this}; - bool ok = TryRecommitSystemPages(address, length, GetPageAccessibility(), - accessibility_disposition); - if (PA_UNLIKELY(!ok)) { - // Decommit some memory and retry. The alternative is crashing. - { - ::partition_alloc::internal::ScopedGuard guard(lock_); - DecommitEmptySlotSpans(); - } - ok = TryRecommitSystemPages(address, length, GetPageAccessibility(), - accessibility_disposition); - } - - if (ok) { - IncreaseCommittedPages(length); - } - - return ok; -} - -// static -// -// Returns the size available to the app. It can be equal or higher than the -// requested size. If higher, the overage won't exceed what's actually usable -// by the app without a risk of running out of an allocated region or into -// PartitionAlloc's internal data. Used as malloc_usable_size and malloc_size. -// -// |ptr| should preferably point to the beginning of an object returned from -// malloc() et al., but it doesn't have to. crbug.com/1292646 shows an example -// where this isn't the case. Note, an inner object pointer won't work for -// direct map, unless it is within the first partition page. -template <bool thread_safe> -PA_ALWAYS_INLINE size_t PartitionRoot<thread_safe>::GetUsableSize(void* ptr) { - // malloc_usable_size() is expected to handle NULL gracefully and return 0. - if (!ptr) { - return 0; - } - auto* slot_span = SlotSpan::FromObjectInnerPtr(ptr); - auto* root = FromSlotSpan(slot_span); - return slot_span->GetUsableSize(root); -} - -template <bool thread_safe> -PA_ALWAYS_INLINE size_t -PartitionRoot<thread_safe>::GetUsableSizeWithMac11MallocSizeHack(void* ptr) { - // malloc_usable_size() is expected to handle NULL gracefully and return 0. - if (!ptr) { - return 0; - } - auto* slot_span = SlotSpan::FromObjectInnerPtr(ptr); - auto* root = FromSlotSpan(slot_span); - size_t usable_size = slot_span->GetUsableSize(root); -#if PA_CONFIG(ENABLE_MAC11_MALLOC_SIZE_HACK) - // Check |mac11_malloc_size_hack_enabled_| flag first as this doesn't - // concern OS versions other than macOS 11. - if (PA_UNLIKELY(root->flags.mac11_malloc_size_hack_enabled_ && - usable_size == internal::kMac11MallocSizeHackUsableSize)) { - uintptr_t slot_start = - internal::PartitionAllocGetSlotStartInBRPPool(UntagPtr(ptr)); - auto* ref_count = internal::PartitionRefCountPointer(slot_start); - if (ref_count->NeedsMac11MallocSizeHack()) { - return internal::kMac11MallocSizeHackRequestedSize; - } - } -#endif // PA_CONFIG(ENABLE_MAC11_MALLOC_SIZE_HACK) - - return usable_size; -} - -// Returns the page configuration to use when mapping slot spans for a given -// partition root. ReadWriteTagged is used on MTE-enabled systems for -// PartitionRoots supporting it. -template <bool thread_safe> -PA_ALWAYS_INLINE PageAccessibilityConfiguration -PartitionRoot<thread_safe>::GetPageAccessibility() const { - PageAccessibilityConfiguration::Permissions permissions = - PageAccessibilityConfiguration::kReadWrite; -#if PA_CONFIG(HAS_MEMORY_TAGGING) - if (IsMemoryTaggingEnabled()) { - permissions = PageAccessibilityConfiguration::kReadWriteTagged; - } -#endif -#if BUILDFLAG(ENABLE_PKEYS) - return PageAccessibilityConfiguration(permissions, flags.pkey); -#else - return PageAccessibilityConfiguration(permissions); -#endif -} - -template <bool thread_safe> -PA_ALWAYS_INLINE PageAccessibilityConfiguration -PartitionRoot<thread_safe>::PageAccessibilityWithPkeyIfEnabled( - PageAccessibilityConfiguration::Permissions permissions) const { -#if BUILDFLAG(ENABLE_PKEYS) - return PageAccessibilityConfiguration(permissions, flags.pkey); -#endif - return PageAccessibilityConfiguration(permissions); -} - -// Return the capacity of the underlying slot (adjusted for extras). This -// doesn't mean this capacity is readily available. It merely means that if -// a new allocation (or realloc) happened with that returned value, it'd use -// the same amount of underlying memory. -template <bool thread_safe> -PA_ALWAYS_INLINE size_t -PartitionRoot<thread_safe>::AllocationCapacityFromSlotStart( - uintptr_t slot_start) const { - auto* slot_span = SlotSpan::FromSlotStart(slot_start); - return AdjustSizeForExtrasSubtract(slot_span->bucket->slot_size); -} - -// static -template <bool thread_safe> -PA_ALWAYS_INLINE uint16_t PartitionRoot<thread_safe>::SizeToBucketIndex( - size_t size, - BucketDistribution bucket_distribution) { - switch (bucket_distribution) { - case BucketDistribution::kDefault: - return internal::BucketIndexLookup::GetIndexForDefaultBuckets(size); - case BucketDistribution::kDenser: - return internal::BucketIndexLookup::GetIndexForDenserBuckets(size); - } -} - -template <bool thread_safe> -PA_ALWAYS_INLINE void* PartitionRoot<thread_safe>::AllocWithFlags( - unsigned int flags, - size_t requested_size, - const char* type_name) { - return AllocWithFlagsInternal(flags, requested_size, - internal::PartitionPageSize(), type_name); -} - -template <bool thread_safe> -PA_ALWAYS_INLINE void* PartitionRoot<thread_safe>::AllocWithFlagsInternal( - unsigned int flags, - size_t requested_size, - size_t slot_span_alignment, - const char* type_name) { - PA_DCHECK( - (slot_span_alignment >= internal::PartitionPageSize()) && - partition_alloc::internal::base::bits::IsPowerOfTwo(slot_span_alignment)); - - PA_DCHECK(flags < AllocFlags::kLastFlag << 1); - PA_DCHECK((flags & AllocFlags::kNoHooks) == 0); // Internal only. - PA_DCHECK(initialized); - -#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - if (!(flags & AllocFlags::kNoMemoryToolOverride)) { - CHECK_MAX_SIZE_OR_RETURN_NULLPTR(requested_size, flags); - const bool zero_fill = flags & AllocFlags::kZeroFill; - void* result = - zero_fill ? calloc(1, requested_size) : malloc(requested_size); - PA_CHECK(result || flags & AllocFlags::kReturnNull); - return result; - } -#endif // defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - void* object = nullptr; - const bool hooks_enabled = PartitionAllocHooks::AreHooksEnabled(); - if (PA_UNLIKELY(hooks_enabled)) { - if (PartitionAllocHooks::AllocationOverrideHookIfEnabled( - &object, flags, requested_size, type_name)) { - PartitionAllocHooks::AllocationObserverHookIfEnabled( - object, requested_size, type_name); - return object; - } - } - - object = AllocWithFlagsNoHooks(flags, requested_size, slot_span_alignment); - - if (PA_UNLIKELY(hooks_enabled)) { - PartitionAllocHooks::AllocationObserverHookIfEnabled(object, requested_size, - type_name); - } - - return object; -} - -template <bool thread_safe> -PA_ALWAYS_INLINE void* PartitionRoot<thread_safe>::AllocWithFlagsNoHooks( - unsigned int flags, - size_t requested_size, - size_t slot_span_alignment) { - PA_DCHECK( - (slot_span_alignment >= internal::PartitionPageSize()) && - partition_alloc::internal::base::bits::IsPowerOfTwo(slot_span_alignment)); - - // The thread cache is added "in the middle" of the main allocator, that is: - // - After all the cookie/ref-count management - // - Before the "raw" allocator. - // - // That is, the general allocation flow is: - // 1. Adjustment of requested size to make room for extras - // 2. Allocation: - // a. Call to the thread cache, if it succeeds, go to step 3. - // b. Otherwise, call the "raw" allocator <-- Locking - // 3. Handle cookie/ref-count, zero allocation if required - - size_t raw_size = AdjustSizeForExtrasAdd(requested_size); - PA_CHECK(raw_size >= requested_size); // check for overflows - - // We should only call |SizeToBucketIndex| at most once when allocating. - // Otherwise, we risk having |bucket_distribution| changed - // underneath us (between calls to |SizeToBucketIndex| during the same call), - // which would result in an inconsistent state. - uint16_t bucket_index = - SizeToBucketIndex(raw_size, this->GetBucketDistribution()); - size_t usable_size; - bool is_already_zeroed = false; - uintptr_t slot_start = 0; - size_t slot_size; - -#if BUILDFLAG(USE_STARSCAN) - const bool is_quarantine_enabled = IsQuarantineEnabled(); - // PCScan safepoint. Call before trying to allocate from cache. - // TODO(bikineev): Change the condition to PA_LIKELY once PCScan is enabled by - // default. - if (PA_UNLIKELY(is_quarantine_enabled)) { - PCScan::JoinScanIfNeeded(); - } -#endif // BUILDFLAG(USE_STARSCAN) - - auto* thread_cache = GetOrCreateThreadCache(); - - // Don't use thread cache if higher order alignment is requested, because the - // thread cache will not be able to satisfy it. - // - // PA_LIKELY: performance-sensitive partitions use the thread cache. - if (PA_LIKELY(ThreadCache::IsValid(thread_cache) && - slot_span_alignment <= internal::PartitionPageSize())) { - // Note: getting slot_size from the thread cache rather than by - // `buckets[bucket_index].slot_size` to avoid touching `buckets` on the fast - // path. - slot_start = thread_cache->GetFromCache(bucket_index, &slot_size); - - // PA_LIKELY: median hit rate in the thread cache is 95%, from metrics. - if (PA_LIKELY(slot_start)) { - // This follows the logic of SlotSpanMetadata::GetUsableSize for small - // buckets, which is too expensive to call here. - // Keep it in sync! - usable_size = AdjustSizeForExtrasSubtract(slot_size); - -#if BUILDFLAG(PA_DCHECK_IS_ON) - // Make sure that the allocated pointer comes from the same place it would - // for a non-thread cache allocation. - SlotSpan* slot_span = SlotSpan::FromSlotStart(slot_start); - PA_DCHECK(IsValidSlotSpan(slot_span)); - PA_DCHECK(slot_span->bucket == &bucket_at(bucket_index)); - PA_DCHECK(slot_span->bucket->slot_size == slot_size); - PA_DCHECK(usable_size == slot_span->GetUsableSize(this)); - // All large allocations must go through the RawAlloc path to correctly - // set |usable_size|. - PA_DCHECK(!slot_span->CanStoreRawSize()); - PA_DCHECK(!slot_span->bucket->is_direct_mapped()); -#endif - } else { - slot_start = - RawAlloc(buckets + bucket_index, flags, raw_size, slot_span_alignment, - &usable_size, &is_already_zeroed); - } - } else { - slot_start = - RawAlloc(buckets + bucket_index, flags, raw_size, slot_span_alignment, - &usable_size, &is_already_zeroed); - } - - if (PA_UNLIKELY(!slot_start)) { - return nullptr; - } - - if (PA_LIKELY(ThreadCache::IsValid(thread_cache))) { - thread_cache->RecordAllocation(usable_size); - } - - // Layout inside the slot: - // |[refcnt]|...object...|[empty]|[cookie]|[unused]| - // <----(a)-----> - // <--------(b)---------> - // <--(c)---> + <--(c)---> - // <---------(d)---------> + <--(d)---> - // <-----------------(e)------------------> - // <----------------------(f)----------------------> - // (a) requested_size - // (b) usable_size - // (c) extras - // (d) raw_size - // (e) utilized_slot_size - // (f) slot_size - // Notes: - // - Ref-count may or may not exist in the slot, depending on brp_enabled(). - // - Cookie exists only in the BUILDFLAG(PA_DCHECK_IS_ON) case. - // - Think of raw_size as the minimum size required internally to satisfy - // the allocation request (i.e. requested_size + extras) - // - Note, at most one "empty" or "unused" space can occur at a time. It - // occurs when slot_size is larger than raw_size. "unused" applies only to - // large allocations (direct-mapped and single-slot slot spans) and "empty" - // only to small allocations. - // Why either-or, one might ask? We make an effort to put the trailing - // cookie as close to data as possible to catch overflows (often - // off-by-one), but that's possible only if we have enough space in metadata - // to save raw_size, i.e. only for large allocations. For small allocations, - // we have no other choice than putting the cookie at the very end of the - // slot, thus creating the "empty" space. - // - // If PUT_REF_COUNT_IN_PREVIOUS_SLOT is set, the layout is: - // |...object...|[empty]|[cookie]|[unused]|[refcnt]| - // <----(a)-----> - // <--------(b)---------> - // <--(c)---> + <--(c)---> - // <----(d)-----> + <--(d)---> + <--(d)---> - // <-------------(e)-------------> + <--(e)---> - // <----------------------(f)----------------------> - // Notes: - // If |slot_start| is not SystemPageSize()-aligned (possible only for small - // allocations), ref-count of this slot is stored at the end of the previous - // slot. Otherwise it is stored in ref-count table placed after the super page - // metadata. For simplicity, the space for ref-count is still reserved at the - // end of previous slot, even though redundant. - - void* object = SlotStartToObject(slot_start); - -#if BUILDFLAG(PA_DCHECK_IS_ON) - // Add the cookie after the allocation. - if (this->flags.allow_cookie) { - internal::PartitionCookieWriteValue(static_cast<unsigned char*>(object) + - usable_size); - } -#endif - - // Fill the region kUninitializedByte (on debug builds, if not requested to 0) - // or 0 (if requested and not 0 already). - bool zero_fill = flags & AllocFlags::kZeroFill; - // PA_LIKELY: operator new() calls malloc(), not calloc(). - if (PA_LIKELY(!zero_fill)) { - // memset() can be really expensive. -#if BUILDFLAG(PA_EXPENSIVE_DCHECKS_ARE_ON) - internal::DebugMemset(object, internal::kUninitializedByte, usable_size); -#endif - } else if (!is_already_zeroed) { - memset(object, 0, usable_size); - } - -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - // TODO(keishi): Add PA_LIKELY when brp is fully enabled as |brp_enabled| will - // be false only for the aligned partition. - if (brp_enabled()) { - bool needs_mac11_malloc_size_hack = false; -#if PA_CONFIG(ENABLE_MAC11_MALLOC_SIZE_HACK) - // Only apply hack to size 32 allocations on macOS 11. There is a buggy - // assertion that malloc_size() equals sizeof(class_rw_t) which is 32. - if (PA_UNLIKELY(this->flags.mac11_malloc_size_hack_enabled_ && - requested_size == - internal::kMac11MallocSizeHackRequestedSize)) { - needs_mac11_malloc_size_hack = true; - } -#endif // PA_CONFIG(ENABLE_MAC11_MALLOC_SIZE_HACK) - auto* ref_count = new (internal::PartitionRefCountPointer(slot_start)) - internal::PartitionRefCount(needs_mac11_malloc_size_hack); -#if PA_CONFIG(REF_COUNT_STORE_REQUESTED_SIZE) - ref_count->SetRequestedSize(requested_size); -#else - (void)ref_count; -#endif - } -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - -#if BUILDFLAG(USE_STARSCAN) - // TODO(bikineev): Change the condition to PA_LIKELY once PCScan is enabled by - // default. - if (PA_UNLIKELY(is_quarantine_enabled)) { - if (PA_LIKELY(internal::IsManagedByNormalBuckets(slot_start))) { - // Mark the corresponding bits in the state bitmap as allocated. - internal::StateBitmapFromAddr(slot_start)->Allocate(slot_start); - } - } -#endif // BUILDFLAG(USE_STARSCAN) - - return object; -} - -template <bool thread_safe> -PA_ALWAYS_INLINE uintptr_t -PartitionRoot<thread_safe>::RawAlloc(Bucket* bucket, - unsigned int flags, - size_t raw_size, - size_t slot_span_alignment, - size_t* usable_size, - bool* is_already_zeroed) { - ::partition_alloc::internal::ScopedGuard guard{lock_}; - return AllocFromBucket(bucket, flags, raw_size, slot_span_alignment, - usable_size, is_already_zeroed); -} - -template <bool thread_safe> -PA_ALWAYS_INLINE void* PartitionRoot<thread_safe>::AlignedAllocWithFlags( - unsigned int flags, - size_t alignment, - size_t requested_size) { - // Aligned allocation support relies on the natural alignment guarantees of - // PartitionAlloc. Specifically, it relies on the fact that slots within a - // slot span are aligned to slot size, from the beginning of the span. - // - // For alignments <=PartitionPageSize(), the code below adjusts the request - // size to be a power of two, no less than alignment. Since slot spans are - // aligned to PartitionPageSize(), which is also a power of two, this will - // automatically guarantee alignment on the adjusted size boundary, thanks to - // the natural alignment described above. - // - // For alignments >PartitionPageSize(), we need to pass the request down the - // stack to only give us a slot span aligned to this more restrictive - // boundary. In the current implementation, this code path will always - // allocate a new slot span and hand us the first slot, so no need to adjust - // the request size. As a consequence, allocating many small objects with - // such a high alignment can cause a non-negligable fragmentation, - // particularly if these allocations are back to back. - // TODO(bartekn): We should check that this is not causing issues in practice. - // - // Extras before the allocation are forbidden as they shift the returned - // allocation from the beginning of the slot, thus messing up alignment. - // Extras after the allocation are acceptable, but they have to be taken into - // account in the request size calculation to avoid crbug.com/1185484. - PA_DCHECK(this->flags.allow_aligned_alloc); - PA_DCHECK(!this->flags.extras_offset); - // This is mandated by |posix_memalign()|, so should never fire. - PA_CHECK(partition_alloc::internal::base::bits::IsPowerOfTwo(alignment)); - // Catch unsupported alignment requests early. - PA_CHECK(alignment <= internal::kMaxSupportedAlignment); - size_t raw_size = AdjustSizeForExtrasAdd(requested_size); - - size_t adjusted_size = requested_size; - if (alignment <= internal::PartitionPageSize()) { - // Handle cases such as size = 16, alignment = 64. - // Wastes memory when a large alignment is requested with a small size, but - // this is hard to avoid, and should not be too common. - if (PA_UNLIKELY(raw_size < alignment)) { - raw_size = alignment; - } else { - // PartitionAlloc only guarantees alignment for power-of-two sized - // allocations. To make sure this applies here, round up the allocation - // size. - raw_size = - static_cast<size_t>(1) - << (int{sizeof(size_t) * 8} - - partition_alloc::internal::base::bits::CountLeadingZeroBits( - raw_size - 1)); - } - PA_DCHECK(partition_alloc::internal::base::bits::IsPowerOfTwo(raw_size)); - // Adjust back, because AllocWithFlagsNoHooks/Alloc will adjust it again. - adjusted_size = AdjustSizeForExtrasSubtract(raw_size); - - // Overflow check. adjusted_size must be larger or equal to requested_size. - if (PA_UNLIKELY(adjusted_size < requested_size)) { - if (flags & AllocFlags::kReturnNull) { - return nullptr; - } - // OutOfMemoryDeathTest.AlignedAlloc requires - // base::TerminateBecauseOutOfMemory (invoked by - // PartitionExcessiveAllocationSize). - internal::PartitionExcessiveAllocationSize(requested_size); - // internal::PartitionExcessiveAllocationSize(size) causes OOM_CRASH. - PA_NOTREACHED(); - } - } - - // Slot spans are naturally aligned on partition page size, but make sure you - // don't pass anything less, because it'll mess up callee's calculations. - size_t slot_span_alignment = - std::max(alignment, internal::PartitionPageSize()); - bool no_hooks = flags & AllocFlags::kNoHooks; - void* object = - no_hooks - ? AllocWithFlagsNoHooks(0, adjusted_size, slot_span_alignment) - : AllocWithFlagsInternal(0, adjusted_size, slot_span_alignment, ""); - - // |alignment| is a power of two, but the compiler doesn't necessarily know - // that. A regular % operation is very slow, make sure to use the equivalent, - // faster form. - // No need to MTE-untag, as it doesn't change alignment. - PA_CHECK(!(reinterpret_cast<uintptr_t>(object) & (alignment - 1))); - - return object; -} - -template <bool thread_safe> -PA_NOINLINE void* PartitionRoot<thread_safe>::Alloc(size_t requested_size, - const char* type_name) { - return AllocWithFlags(0, requested_size, type_name); -} - -template <bool thread_safe> -PA_NOINLINE void* PartitionRoot<thread_safe>::Realloc(void* ptr, - size_t new_size, - const char* type_name) { - return ReallocWithFlags(0, ptr, new_size, type_name); -} - -template <bool thread_safe> -PA_NOINLINE void* PartitionRoot<thread_safe>::TryRealloc( - void* ptr, - size_t new_size, - const char* type_name) { - return ReallocWithFlags(AllocFlags::kReturnNull, ptr, new_size, type_name); -} - -// Return the capacity of the underlying slot (adjusted for extras) that'd be -// used to satisfy a request of |size|. This doesn't mean this capacity would be -// readily available. It merely means that if an allocation happened with that -// returned value, it'd use the same amount of underlying memory as the -// allocation with |size|. -template <bool thread_safe> -PA_ALWAYS_INLINE size_t -PartitionRoot<thread_safe>::AllocationCapacityFromRequestedSize( - size_t size) const { -#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - return size; -#else - PA_DCHECK(PartitionRoot<thread_safe>::initialized); - size = AdjustSizeForExtrasAdd(size); - auto& bucket = bucket_at(SizeToBucketIndex(size, GetBucketDistribution())); - PA_DCHECK(!bucket.slot_size || bucket.slot_size >= size); - PA_DCHECK(!(bucket.slot_size % internal::kSmallestBucket)); - - if (PA_LIKELY(!bucket.is_direct_mapped())) { - size = bucket.slot_size; - } else if (size > internal::MaxDirectMapped()) { - // Too large to allocate => return the size unchanged. - } else { - size = GetDirectMapSlotSize(size); - } - size = AdjustSizeForExtrasSubtract(size); - return size; -#endif -} - -template <bool thread_safe> -ThreadCache* PartitionRoot<thread_safe>::GetOrCreateThreadCache() { - ThreadCache* thread_cache = nullptr; - if (PA_LIKELY(flags.with_thread_cache)) { - thread_cache = ThreadCache::Get(); - if (PA_UNLIKELY(!ThreadCache::IsValid(thread_cache))) { - thread_cache = MaybeInitThreadCache(); - } - } - return thread_cache; -} - -template <bool thread_safe> -ThreadCache* PartitionRoot<thread_safe>::GetThreadCache() { - return PA_LIKELY(flags.with_thread_cache) ? ThreadCache::Get() : nullptr; -} - -using ThreadSafePartitionRoot = PartitionRoot<internal::ThreadSafe>; - -static_assert(offsetof(ThreadSafePartitionRoot, lock_) == - internal::kPartitionCachelineSize, - "Padding is incorrect"); - -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) -// Usage in `raw_ptr.cc` is notable enough to merit a non-internal alias. -using ::partition_alloc::internal::PartitionAllocGetSlotStartInBRPPool; -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - -} // namespace partition_alloc - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ROOT_H_
diff --git a/base/allocator/partition_allocator/partition_stats.cc b/base/allocator/partition_allocator/partition_stats.cc deleted file mode 100644 index 90cb336..0000000 --- a/base/allocator/partition_allocator/partition_stats.cc +++ /dev/null
@@ -1,21 +0,0 @@ -// 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. - -#include "base/allocator/partition_allocator/partition_stats.h" - -#include <cstring> - -namespace partition_alloc { - -SimplePartitionStatsDumper::SimplePartitionStatsDumper() { - memset(&stats_, 0, sizeof(stats_)); -} - -void SimplePartitionStatsDumper::PartitionDumpTotals( - const char* partition_name, - const PartitionMemoryStats* memory_stats) { - stats_ = *memory_stats; -} - -} // namespace partition_alloc
diff --git a/base/allocator/partition_allocator/partition_stats.h b/base/allocator/partition_allocator/partition_stats.h deleted file mode 100644 index 82158ca..0000000 --- a/base/allocator/partition_allocator/partition_stats.h +++ /dev/null
@@ -1,151 +0,0 @@ -// 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 BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_STATS_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_STATS_H_ - -#include <cstddef> -#include <cstdint> - -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_config.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" - -namespace partition_alloc { - -// Most of these are not populated if PA_THREAD_CACHE_ENABLE_STATISTICS is not -// defined. -struct ThreadCacheStats { - uint64_t alloc_count; // Total allocation requests. - uint64_t alloc_hits; // Thread cache hits. - uint64_t alloc_misses; // Thread cache misses. - - // Allocation failure details: - uint64_t alloc_miss_empty; - uint64_t alloc_miss_too_large; - - // Cache fill details: - uint64_t cache_fill_count; - uint64_t cache_fill_hits; - uint64_t cache_fill_misses; // Object too large. - - uint64_t batch_fill_count; // Number of central allocator requests. - - // Memory cost: - uint32_t bucket_total_memory; - uint32_t metadata_overhead; - -#if PA_CONFIG(THREAD_CACHE_ALLOC_STATS) - uint64_t allocs_per_bucket_[internal::kNumBuckets + 1]; -#endif // PA_CONFIG(THREAD_CACHE_ALLOC_STATS) -}; - -// Per-thread allocation statistics. Only covers allocations made through the -// partition linked to the thread cache. As the allocator doesn't record -// requested sizes in most cases, the data there will be an overestimate of the -// actually requested sizes. It is also not expected to sum up to anything -// meaningful across threads, due to the lack of synchronization. Figures there -// are cumulative, not net. Since the data below is per-thread, note a thread -// can deallocate more than it allocated. -struct ThreadAllocStats { - uint64_t alloc_count; - uint64_t alloc_total_size; - uint64_t dealloc_count; - uint64_t dealloc_total_size; -}; - -// Struct used to retrieve total memory usage of a partition. Used by -// PartitionStatsDumper implementation. -struct PartitionMemoryStats { - size_t total_mmapped_bytes; // Total bytes mmap()-ed from the system. - size_t total_committed_bytes; // Total size of committed pages. - size_t max_committed_bytes; // Max size of committed pages. - size_t total_allocated_bytes; // Total size of allcoations. - size_t max_allocated_bytes; // Max size of allocations. - size_t total_resident_bytes; // Total bytes provisioned by the partition. - size_t total_active_bytes; // Total active bytes in the partition. - size_t total_active_count; // Total count of active objects in the partition. - size_t total_decommittable_bytes; // Total bytes that could be decommitted. - size_t total_discardable_bytes; // Total bytes that could be discarded. -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - size_t - total_brp_quarantined_bytes; // Total bytes that are quarantined by BRP. - size_t total_brp_quarantined_count; // Total number of slots that are - // quarantined by BRP. - size_t cumulative_brp_quarantined_bytes; // Cumulative bytes that are - // quarantined by BRP. - size_t cumulative_brp_quarantined_count; // Cumulative number of slots that - // are quarantined by BRP. -#endif - - bool has_thread_cache; - ThreadCacheStats current_thread_cache_stats; - ThreadCacheStats all_thread_caches_stats; - - // Count and total duration of system calls made since process start. May not - // be reported on all platforms. - uint64_t syscall_count; - uint64_t syscall_total_time_ns; -}; - -// Struct used to retrieve memory statistics about a partition bucket. Used by -// PartitionStatsDumper implementation. -struct PartitionBucketMemoryStats { - bool is_valid; // Used to check if the stats is valid. - bool is_direct_map; // True if this is a direct mapping; size will not be - // unique. - uint32_t bucket_slot_size; // The size of the slot in bytes. - uint32_t allocated_slot_span_size; // Total size the slot span allocated - // from the system (committed pages). - uint32_t active_bytes; // Total active bytes used in the bucket. - uint32_t active_count; // Total active objects allocated in the bucket. - uint32_t resident_bytes; // Total bytes provisioned in the bucket. - uint32_t decommittable_bytes; // Total bytes that could be decommitted. - uint32_t discardable_bytes; // Total bytes that could be discarded. - uint32_t num_full_slot_spans; // Number of slot spans with all slots - // allocated. - uint32_t num_active_slot_spans; // Number of slot spans that have at least - // one provisioned slot. - uint32_t num_empty_slot_spans; // Number of slot spans that are empty - // but not decommitted. - uint32_t num_decommitted_slot_spans; // Number of slot spans that are empty - // and decommitted. -}; - -// Interface that is passed to PartitionDumpStats and -// PartitionDumpStats for using the memory statistics. -class PA_COMPONENT_EXPORT(PARTITION_ALLOC) PartitionStatsDumper { - public: - // Called to dump total memory used by partition, once per partition. - virtual void PartitionDumpTotals(const char* partition_name, - const PartitionMemoryStats*) = 0; - - // Called to dump stats about buckets, for each bucket. - virtual void PartitionsDumpBucketStats(const char* partition_name, - const PartitionBucketMemoryStats*) = 0; -}; - -// Simple version of PartitionStatsDumper, storing the returned stats in stats_. -// Does not handle per-bucket stats. -class PA_COMPONENT_EXPORT(PARTITION_ALLOC) SimplePartitionStatsDumper - : public PartitionStatsDumper { - public: - SimplePartitionStatsDumper(); - - void PartitionDumpTotals(const char* partition_name, - const PartitionMemoryStats* memory_stats) override; - - void PartitionsDumpBucketStats(const char* partition_name, - const PartitionBucketMemoryStats*) override {} - - const PartitionMemoryStats& stats() const { return stats_; } - - private: - PartitionMemoryStats stats_; -}; - -} // namespace partition_alloc - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_STATS_H_
diff --git a/base/allocator/partition_allocator/partition_tls.h b/base/allocator/partition_allocator/partition_tls.h deleted file mode 100644 index 420559f..0000000 --- a/base/allocator/partition_allocator/partition_tls.h +++ /dev/null
@@ -1,147 +0,0 @@ -// 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 BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_TLS_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_TLS_H_ - -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "build/build_config.h" - -#if BUILDFLAG(IS_POSIX) -#include <pthread.h> -#endif - -#if BUILDFLAG(IS_WIN) -#include "base/allocator/partition_allocator/partition_alloc_base/win/windows_types.h" -#endif - -// Barebones TLS implementation for use in PartitionAlloc. This doesn't use the -// general chromium TLS handling to avoid dependencies, but more importantly -// because it allocates memory. -namespace partition_alloc::internal { - -#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) -using PartitionTlsKey = pthread_key_t; - -// Only on x86_64, the implementation is not stable on ARM64. For instance, in -// macOS 11, the TPIDRRO_EL0 registers holds the CPU index in the low bits, -// which is not the case in macOS 12. See libsyscall/os/tsd.h in XNU -// (_os_tsd_get_direct() is used by pthread_getspecific() internally). -#if BUILDFLAG(IS_MAC) && defined(ARCH_CPU_X86_64) -namespace { - -PA_ALWAYS_INLINE void* FastTlsGet(PartitionTlsKey index) { - // On macOS, pthread_getspecific() is in libSystem, so a call to it has to go - // through PLT. However, and contrary to some other platforms, *all* TLS keys - // are in a static array in the thread structure. So they are *always* at a - // fixed offset from the segment register holding the thread structure - // address. - // - // We could use _pthread_getspecific_direct(), but it is not - // exported. However, on all macOS versions we support, the TLS array is at - // %gs. This is used in V8 to back up InternalGetExistingThreadLocal(), and - // can also be seen by looking at pthread_getspecific() disassembly: - // - // libsystem_pthread.dylib`pthread_getspecific: - // libsystem_pthread.dylib[0x7ff800316099] <+0>: movq %gs:(,%rdi,8), %rax - // libsystem_pthread.dylib[0x7ff8003160a2] <+9>: retq - // - // This function is essentially inlining the content of pthread_getspecific() - // here. - intptr_t result; - static_assert(sizeof index <= sizeof(intptr_t)); - asm("movq %%gs:(,%1,8), %0;" - : "=r"(result) - : "r"(static_cast<intptr_t>(index))); - - return reinterpret_cast<void*>(result); -} - -} // namespace -#endif // BUILDFLAG(IS_MAC) && defined(ARCH_CPU_X86_64) - -PA_ALWAYS_INLINE bool PartitionTlsCreate(PartitionTlsKey* key, - void (*destructor)(void*)) { - return !pthread_key_create(key, destructor); -} - -PA_ALWAYS_INLINE void* PartitionTlsGet(PartitionTlsKey key) { -#if BUILDFLAG(IS_MAC) && defined(ARCH_CPU_X86_64) - PA_DCHECK(pthread_getspecific(key) == FastTlsGet(key)); - return FastTlsGet(key); -#else - return pthread_getspecific(key); -#endif -} - -PA_ALWAYS_INLINE void PartitionTlsSet(PartitionTlsKey key, void* value) { - int ret = pthread_setspecific(key, value); - PA_DCHECK(!ret); -} - -#elif BUILDFLAG(IS_WIN) -// Note: supports only a single TLS key on Windows. Not a hard constraint, may -// be lifted. -using PartitionTlsKey = unsigned long; - -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -bool PartitionTlsCreate(PartitionTlsKey* key, void (*destructor)(void*)); - -PA_ALWAYS_INLINE void* PartitionTlsGet(PartitionTlsKey key) { - // Accessing TLS resets the last error, which then makes |GetLastError()| - // return something misleading. While this means that properly using - // |GetLastError()| is difficult, there is currently code in Chromium which - // expects malloc() to *not* reset it. Meaning that we either have to fix this - // code, or pay the cost of saving/restoring it. - // - // Source: - // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-tlsgetvalue - // "Functions that return indications of failure call SetLastError() when they - // fail. They generally do not call SetLastError() when they succeed. The - // TlsGetValue() function is an exception to this general rule. The - // TlsGetValue() function calls SetLastError() to clear a thread's last error - // when it succeeds." - DWORD saved_error = GetLastError(); - void* ret = TlsGetValue(key); - // Only non-zero errors need to be restored. - if (PA_UNLIKELY(saved_error)) { - SetLastError(saved_error); - } - return ret; -} - -PA_ALWAYS_INLINE void PartitionTlsSet(PartitionTlsKey key, void* value) { - BOOL ret = TlsSetValue(key, value); - PA_DCHECK(ret); -} - -// Registers a callback for DLL_PROCESS_DETACH events. -void PartitionTlsSetOnDllProcessDetach(void (*callback)()); - -#else -// Not supported. -using PartitionTlsKey = int; - -PA_ALWAYS_INLINE bool PartitionTlsCreate(PartitionTlsKey* key, - void (*destructor)(void*)) { - // NOTIMPLEMENTED() may allocate, crash instead. - PA_IMMEDIATE_CRASH(); -} - -PA_ALWAYS_INLINE void* PartitionTlsGet(PartitionTlsKey key) { - PA_IMMEDIATE_CRASH(); -} - -PA_ALWAYS_INLINE void PartitionTlsSet(PartitionTlsKey key, void* value) { - PA_IMMEDIATE_CRASH(); -} - -#endif // BUILDFLAG(IS_WIN) - -} // namespace partition_alloc::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_TLS_H_
diff --git a/base/allocator/partition_allocator/partition_tls_win.cc b/base/allocator/partition_allocator/partition_tls_win.cc deleted file mode 100644 index 5f1b161..0000000 --- a/base/allocator/partition_allocator/partition_tls_win.cc +++ /dev/null
@@ -1,113 +0,0 @@ -// 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. - -#include "base/allocator/partition_allocator/partition_tls.h" - -#include <windows.h> - -namespace partition_alloc::internal { - -namespace { - -// Store the key as the thread destruction callback doesn't get it. -PartitionTlsKey g_key; -void (*g_destructor)(void*) = nullptr; -void (*g_on_dll_process_detach)() = nullptr; - -// Static callback function to call with each thread termination. -void NTAPI PartitionTlsOnThreadExit(PVOID module, - DWORD reason, - PVOID reserved) { - if (reason != DLL_THREAD_DETACH && reason != DLL_PROCESS_DETACH) { - return; - } - - if (reason == DLL_PROCESS_DETACH && g_on_dll_process_detach) { - g_on_dll_process_detach(); - } - - if (g_destructor) { - void* per_thread_data = PartitionTlsGet(g_key); - if (per_thread_data) { - g_destructor(per_thread_data); - } - } -} - -} // namespace - -bool PartitionTlsCreate(PartitionTlsKey* key, void (*destructor)(void*)) { - PA_CHECK(g_destructor == nullptr); // Only one TLS key supported at a time. - PartitionTlsKey value = TlsAlloc(); - if (value != TLS_OUT_OF_INDEXES) { - *key = value; - - g_key = value; - g_destructor = destructor; - return true; - } - return false; -} - -void PartitionTlsSetOnDllProcessDetach(void (*callback)()) { - g_on_dll_process_detach = callback; -} - -} // namespace partition_alloc::internal - -// See thread_local_storage_win.cc for details and reference. -// -// The callback has to be in any section between .CRT$XLA and .CRT$XLZ, as these -// are sentinels used by the TLS code to find the callback array bounds. As we -// don't particularly care about where we are called but would prefer to be -// deinitialized towards the end (in particular after Chromium's TLS), we locate -// ourselves in .CRT$XLY. - -// Force a reference to _tls_used to make the linker create the TLS directory if -// it's not already there. (e.g. if __declspec(thread) is not used). Force a -// reference to partition_tls_thread_exit_callback to prevent whole program -// optimization from discarding the variable. -#ifdef _WIN64 - -#pragma comment(linker, "/INCLUDE:_tls_used") -#pragma comment(linker, "/INCLUDE:partition_tls_thread_exit_callback") - -#else // _WIN64 - -#pragma comment(linker, "/INCLUDE:__tls_used") -#pragma comment(linker, "/INCLUDE:_partition_tls_thread_exit_callback") - -#endif // _WIN64 - -// extern "C" suppresses C++ name mangling so we know the symbol name for the -// linker /INCLUDE:symbol pragma above. -extern "C" { -// The linker must not discard partition_tls_thread_exit_callback. (We force a -// reference to this variable with a linker /INCLUDE:symbol pragma to ensure -// that.) If this variable is discarded, PartitionTlsOnThreadExit will never be -// called. -#ifdef _WIN64 - -// .CRT section is merged with .rdata on x64 so it must be constant data. -#pragma const_seg(".CRT$XLY") -// When defining a const variable, it must have external linkage to be sure the -// linker doesn't discard it. -extern const PIMAGE_TLS_CALLBACK partition_tls_thread_exit_callback; -const PIMAGE_TLS_CALLBACK partition_tls_thread_exit_callback = - partition_alloc::internal::PartitionTlsOnThreadExit; - -// Reset the default section. -#pragma const_seg() - -#else // _WIN64 - -#pragma data_seg(".CRT$XLY") -PIMAGE_TLS_CALLBACK partition_tls_thread_exit_callback = - partition_alloc::internal::PartitionTlsOnThreadExit; - -// Reset the default section. -#pragma data_seg() - -#endif // _WIN64 -} // extern "C"
diff --git a/base/allocator/partition_allocator/pkey.cc b/base/allocator/partition_allocator/pkey.cc deleted file mode 100644 index de5f1fe..0000000 --- a/base/allocator/partition_allocator/pkey.cc +++ /dev/null
@@ -1,123 +0,0 @@ -// 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. - -#include "base/allocator/partition_allocator/pkey.h" -#include "base/allocator/partition_allocator/address_pool_manager.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "base/allocator/partition_allocator/reservation_offset_table.h" - -#if BUILDFLAG(ENABLE_PKEYS) - -#include <errno.h> -#include <sys/mman.h> -#include <sys/syscall.h> -#include <unistd.h> - -#include "base/allocator/partition_allocator/partition_alloc_base/cpu.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" - -#if !BUILDFLAG(IS_LINUX) -#error "This pkey code is currently only supported on Linux" -#endif - -namespace partition_alloc::internal { - -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -bool CPUHasPkeySupport() { - return base::CPU::GetInstanceNoAllocation().has_pku(); -} - -PkeySettings PkeySettings::settings PA_PKEY_ALIGN; - -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -int PkeyMprotect(void* addr, size_t len, int prot, int pkey) { - return syscall(SYS_pkey_mprotect, addr, len, prot, pkey); -} - -int PkeyMprotectIfEnabled(void* addr, size_t len, int prot, int pkey) { - if (PA_UNLIKELY(PkeySettings::settings.enabled)) { - return PkeyMprotect(addr, len, prot, pkey); - } - - PA_CHECK(pkey == 0); - - return mprotect(addr, len, prot); -} - -void TagMemoryWithPkey(int pkey, void* address, size_t size) { - PA_DCHECK( - (reinterpret_cast<uintptr_t>(address) & PA_PKEY_ALIGN_OFFSET_MASK) == 0); - PA_PCHECK( - PkeyMprotect(address, - (size + PA_PKEY_ALIGN_OFFSET_MASK) & PA_PKEY_ALIGN_BASE_MASK, - PROT_READ | PROT_WRITE, pkey) == 0); -} - -template <typename T> -void TagVariableWithPkey(int pkey, T& var) { - TagMemoryWithPkey(pkey, &var, sizeof(T)); -} - -void TagGlobalsWithPkey(int pkey) { - TagVariableWithPkey(pkey, PartitionAddressSpace::setup_); - - AddressPoolManager::Pool* pool = - AddressPoolManager::GetInstance().GetPool(kPkeyPoolHandle); - TagVariableWithPkey(pkey, *pool); - - uint16_t* pkey_reservation_offset_table = - GetReservationOffsetTable(kPkeyPoolHandle); - TagMemoryWithPkey(pkey, pkey_reservation_offset_table, - ReservationOffsetTable::kReservationOffsetTableLength); - - TagVariableWithPkey(pkey, PkeySettings::settings); -} - -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -int PkeyAlloc(int access_rights) { - return syscall(SYS_pkey_alloc, 0, access_rights); -} - -PA_COMPONENT_EXPORT(PARTITION_ALLOC) -void PkeyFree(int pkey) { - PA_PCHECK(syscall(SYS_pkey_free, pkey) == 0); -} - -uint32_t Rdpkru() { - uint32_t pkru; - asm volatile(".byte 0x0f,0x01,0xee\n" : "=a"(pkru) : "c"(0), "d"(0)); - return pkru; -} - -void Wrpkru(uint32_t pkru) { - asm volatile(".byte 0x0f,0x01,0xef\n" : : "a"(pkru), "c"(0), "d"(0)); -} - -#if BUILDFLAG(PA_DCHECK_IS_ON) - -LiftPkeyRestrictionsScope::LiftPkeyRestrictionsScope() - : saved_pkey_value_(kDefaultPkeyValue) { - if (!PkeySettings::settings.enabled) { - return; - } - saved_pkey_value_ = Rdpkru(); - if (saved_pkey_value_ != kDefaultPkeyValue) { - Wrpkru(kAllowAllPkeyValue); - } -} - -LiftPkeyRestrictionsScope::~LiftPkeyRestrictionsScope() { - if (!PkeySettings::settings.enabled) { - return; - } - if (Rdpkru() != saved_pkey_value_) { - Wrpkru(saved_pkey_value_); - } -} - -#endif // BUILDFLAG(PA_DCHECK_IS_ON) - -} // namespace partition_alloc::internal - -#endif // BUILDFLAG(ENABLE_PKEYS)
diff --git a/base/allocator/partition_allocator/pkey.h b/base/allocator/partition_allocator/pkey.h deleted file mode 100644 index 2c91f44..0000000 --- a/base/allocator/partition_allocator/pkey.h +++ /dev/null
@@ -1,98 +0,0 @@ -// 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. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PKEY_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PKEY_H_ - -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" - -#if BUILDFLAG(ENABLE_PKEYS) - -#include "base/allocator/partition_allocator/page_allocator_constants.h" -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" - -#include <cstddef> -#include <cstdint> - -#if !BUILDFLAG(HAS_64_BIT_POINTERS) -#error "pkey support requires 64 bit pointers" -#endif - -#define PA_PKEY_ALIGN_SZ SystemPageSize() -#define PA_PKEY_ALIGN_OFFSET_MASK (PA_PKEY_ALIGN_SZ - 1) -#define PA_PKEY_ALIGN_BASE_MASK (~PA_PKEY_ALIGN_OFFSET_MASK) -#define PA_PKEY_ALIGN alignas(PA_PKEY_ALIGN_SZ) - -#define PA_PKEY_FILL_PAGE_SZ(size) \ - ((PA_PKEY_ALIGN_SZ - (size & PA_PKEY_ALIGN_OFFSET_MASK)) % PA_PKEY_ALIGN_SZ) -// Calculate the required padding so that the last element of a page-aligned -// array lands on a page boundary. In other words, calculate that padding so -// that (count-1) elements are a multiple of page size. -#define PA_PKEY_ARRAY_PAD_SZ(Type, count) \ - PA_PKEY_FILL_PAGE_SZ(sizeof(Type) * (count - 1)) - -namespace partition_alloc::internal { - -constexpr int kDefaultPkey = 0; -constexpr int kInvalidPkey = -1; - -// Check if the CPU supports pkeys. -bool CPUHasPkeySupport(); - -// A wrapper around pkey_mprotect that falls back to regular mprotect if -// PkeySettings::enabled is false. -[[nodiscard]] int PkeyMprotectIfEnabled(void* addr, - size_t len, - int prot, - int pkey); -// A wrapper around pkey_mprotect without fallback. -[[nodiscard]] int PkeyMprotect(void* addr, size_t len, int prot, int pkey); - -// If we set up a pkey pool, we need to tag global variables with the pkey to -// make them readable in case default pkey access is disabled. Called once -// during pkey pool initialization. -void TagGlobalsWithPkey(int pkey); - -int PkeyAlloc(int access_rights); - -void PkeyFree(int pkey); - -// Read the pkru register (the current pkey state). -uint32_t Rdpkru(); - -// Write the pkru register (the current pkey state). -void Wrpkru(uint32_t pkru); - -struct PkeySettings { - bool enabled = false; - char pad_[PA_PKEY_FILL_PAGE_SZ(sizeof(enabled))] = {}; - static PkeySettings settings PA_PKEY_ALIGN PA_CONSTINIT; -}; - -#if BUILDFLAG(PA_DCHECK_IS_ON) - -class PA_COMPONENT_EXPORT(PARTITION_ALLOC) LiftPkeyRestrictionsScope { - public: - static constexpr uint32_t kDefaultPkeyValue = 0x55555554; - static constexpr uint32_t kAllowAllPkeyValue = 0x0; - - LiftPkeyRestrictionsScope(); - ~LiftPkeyRestrictionsScope(); - - private: - uint32_t saved_pkey_value_; -}; - -#endif // BUILDFLAG(PA_DCHECK_IS_ON) - -} // namespace partition_alloc::internal - -#else // BUILDFLAG(ENABLE_PKEYS) -#define PA_PKEY_ALIGN -#define PA_PKEY_FILL_PAGE_SZ(size) 0 -#define PA_PKEY_ARRAY_PAD_SZ(Type, size) 0 -#endif // BUILDFLAG(ENABLE_PKEYS) - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PKEY_H_
diff --git a/base/allocator/partition_allocator/pkey_unittest.cc b/base/allocator/partition_allocator/pkey_unittest.cc deleted file mode 100644 index 6c1ab64..0000000 --- a/base/allocator/partition_allocator/pkey_unittest.cc +++ /dev/null
@@ -1,253 +0,0 @@ -// 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. - -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" - -#if BUILDFLAG(ENABLE_PKEYS) - -#include <link.h> -#include <sys/mman.h> -#include <sys/syscall.h> - -#include "base/allocator/partition_allocator/page_allocator.h" -#include "base/allocator/partition_allocator/page_allocator_constants.h" -#include "base/allocator/partition_allocator/partition_alloc.h" -#include "base/allocator/partition_allocator/partition_alloc_base/no_destructor.h" -#include "base/allocator/partition_allocator/partition_alloc_forward.h" -#include "base/allocator/partition_allocator/pkey.h" -#include "testing/gtest/include/gtest/gtest.h" - -#define ISOLATED_FUNCTION extern "C" __attribute__((used)) -constexpr size_t kIsolatedThreadStackSize = 64 * 1024; -constexpr int kNumPkey = 16; -constexpr size_t kTestReturnValue = 0x8765432187654321llu; - -namespace partition_alloc::internal { - -struct IsolatedGlobals { - int pkey = kInvalidPkey; - void* isolatedStack; - partition_alloc::internal::base::NoDestructor< - partition_alloc::PartitionAllocator> - allocator{}; - partition_alloc::ThreadSafePartitionRoot* allocatorRoot; -} isolatedGlobals PA_PKEY_ALIGN; - -int ProtFromSegmentFlags(ElfW(Word) flags) { - int prot = 0; - if (flags & PF_R) { - prot |= PROT_READ; - } - if (flags & PF_W) { - prot |= PROT_WRITE; - } - if (flags & PF_X) { - prot |= PROT_EXEC; - } - return prot; -} - -int ProtectROSegments(struct dl_phdr_info* info, size_t info_size, void* data) { - if (!strcmp(info->dlpi_name, "linux-vdso.so.1")) { - return 0; - } - for (int i = 0; i < info->dlpi_phnum; i++) { - const ElfW(Phdr)* phdr = &info->dlpi_phdr[i]; - if (phdr->p_type != PT_LOAD && phdr->p_type != PT_GNU_RELRO) { - continue; - } - if (phdr->p_flags & PF_W) { - continue; - } - uintptr_t start = info->dlpi_addr + phdr->p_vaddr; - uintptr_t end = start + phdr->p_memsz; - uintptr_t startPage = RoundDownToSystemPage(start); - uintptr_t endPage = RoundUpToSystemPage(end); - uintptr_t size = endPage - startPage; - PA_PCHECK(PkeyMprotect(reinterpret_cast<void*>(startPage), size, - ProtFromSegmentFlags(phdr->p_flags), - isolatedGlobals.pkey) == 0); - } - return 0; -} - -class PkeyTest : public testing::Test { - protected: - void PkeyProtectMemory() { - PA_PCHECK(dl_iterate_phdr(ProtectROSegments, nullptr) == 0); - - PA_PCHECK(PkeyMprotect(&isolatedGlobals, sizeof(isolatedGlobals), - PROT_READ | PROT_WRITE, isolatedGlobals.pkey) == 0); - - PA_PCHECK(PkeyMprotect(isolatedGlobals.isolatedStack, - kIsolatedThreadStackSize, PROT_READ | PROT_WRITE, - isolatedGlobals.pkey) == 0); - } - - void InitializeIsolatedThread() { - isolatedGlobals.isolatedStack = - mmap(nullptr, kIsolatedThreadStackSize, PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE | MAP_STACK, -1, 0); - PA_PCHECK(isolatedGlobals.isolatedStack != MAP_FAILED); - - PkeyProtectMemory(); - } - - void SetUp() override { - int pkey = PkeyAlloc(0); - if (pkey == -1) { - return; - } - isolatedGlobals.pkey = pkey; - - isolatedGlobals.allocator->init({ - partition_alloc::PartitionOptions::AlignedAlloc::kAllowed, - partition_alloc::PartitionOptions::ThreadCache::kDisabled, - partition_alloc::PartitionOptions::Quarantine::kDisallowed, - partition_alloc::PartitionOptions::Cookie::kAllowed, - partition_alloc::PartitionOptions::BackupRefPtr::kDisabled, - partition_alloc::PartitionOptions::BackupRefPtrZapping::kDisabled, - partition_alloc::PartitionOptions::UseConfigurablePool::kNo, - partition_alloc::PartitionOptions::AddDummyRefCount::kDisabled, - isolatedGlobals.pkey, - }); - isolatedGlobals.allocatorRoot = isolatedGlobals.allocator->root(); - - InitializeIsolatedThread(); - } - - void TearDown() override { - if (isolatedGlobals.pkey == kInvalidPkey) { - return; - } - PA_PCHECK(PkeyMprotect(&isolatedGlobals, sizeof(isolatedGlobals), - PROT_READ | PROT_WRITE, kDefaultPkey) == 0); - isolatedGlobals.pkey = kDefaultPkey; - InitializeIsolatedThread(); - PkeyFree(isolatedGlobals.pkey); - } -}; - -// This code will run with access limited to pkey 1, no default pkey access. -// Note that we're stricter than required for debugging purposes. -// In the final use, we'll likely allow at least read access to the default -// pkey. -ISOLATED_FUNCTION uint64_t IsolatedAllocFree(void* arg) { - char* buf = (char*)isolatedGlobals.allocatorRoot->AllocWithFlagsNoHooks( - 0, 1024, partition_alloc::PartitionPageSize()); - if (!buf) { - return 0xffffffffffffffffllu; - } - isolatedGlobals.allocatorRoot->FreeNoHooks(buf); - - return kTestReturnValue; -} - -// This test is a bit compliated. We want to ensure that the code -// allocating/freeing from the pkey pool doesn't *unexpectedly* access memory -// tagged with the default pkey (pkey 0). This could be a security issue since -// in our CFI threat model that memory might be attacker controlled. -// To test for this, we run alloc/free without access to the default pkey. In -// order to do this, we need to tag all global read-only memory with our pkey as -// well as switch to a pkey-tagged stack. -TEST_F(PkeyTest, AllocWithoutDefaultPkey) { - if (isolatedGlobals.pkey == kInvalidPkey) { - return; - } - - uint64_t ret; - uint32_t pkru_value = 0; - for (int pkey = 0; pkey < kNumPkey; pkey++) { - if (pkey != isolatedGlobals.pkey) { - pkru_value |= (PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE) << (2 * pkey); - } - } - - // Switch to the safe stack with inline assembly. - // - // The simple solution would be to use one asm statement as a prologue to - // switch to the protected stack and a second one to switch it back. However, - // that doesn't work since inline assembly doesn't support a clobbered stack - // register. So instead, we switch the stack, perform a function call - // to the - // actual code and switch back afterwards. - // - // The inline asm docs mention that special care must be taken - // when calling a function in inline assembly. I.e. we will - // need to make sure that we follow the ABI of the platform. - // In this example, we use the System-V ABI. - // - // == Caller-saved registers == - // We had two ideas for handling caller-saved registers. Option 1 was chosen, - // but I'll describe both to show why option 2 didn't work out: - // * Option 1) mark all caller-saved registers as clobbered. This should be - // in line with how the compiler would create the function call. - // Problem: future additions to caller-saved registers can break - // this. - // * Option 2) use attribute no_caller_saved_registers. This prohibits use of - // sse/mmx/x87. We can disable sse/mmx with a "target" attribute, - // but I couldn't find a way to disable x87. - // The docs tell you to use -mgeneral-regs-only. Maybe we - // could move the isolated code to a separate file and then - // use that flag for compiling that file only. - // !!! This doesn't work: the inner function can call out to code - // that uses caller-saved registers and won't save - // them itself. - // - // == stack alignment == - // The ABI requires us to have a 16 byte aligned rsp on function - // entry. We push one qword onto the stack so we need to subtract - // an additional 8 bytes from the stack pointer. - // - // == additional clobbering == - // As described above, we need to clobber everything besides - // callee-saved registers. The ABI requires all x87 registers to - // be set to empty on fn entry / return, - // so we should tell the compiler that this is the case. As I understand the - // docs, this is done by marking them as clobbered. Worst case, we'll notice - // any issues quickly and can fix them if it turned out to be false> - // - // == direction flag == - // Theoretically, the DF flag could be set to 1 at asm entry. If this - // leads to problems, we might have to zero it before the fn call and - // restore it afterwards. I would'ave assumed that marking flags as - // clobbered would require the compiler to reset the DF before the next fn - // call, but that doesn't seem to be the case. - asm volatile( - // Set pkru to only allow access to pkey 1 memory. - ".byte 0x0f,0x01,0xef\n" // wrpkru - - // Move to the isolated stack and store the old value - "xchg %4, %%rsp\n" - "push %4\n" - "call IsolatedAllocFree\n" - // We need rax below, so move the return value to the stack - "push %%rax\n" - - // Set pkru to only allow access to pkey 0 memory. - "mov $0b10101010101010101010101010101000, %%rax\n" - "xor %%rcx, %%rcx\n" - "xor %%rdx, %%rdx\n" - ".byte 0x0f,0x01,0xef\n" // wrpkru - - // Pop the return value - "pop %0\n" - // Restore the original stack - "pop %%rsp\n" - - : "=r"(ret) - : "a"(pkru_value), "c"(0), "d"(0), - "r"(reinterpret_cast<uintptr_t>(isolatedGlobals.isolatedStack) + - kIsolatedThreadStackSize - 8) - : "memory", "cc", "r8", "r9", "r10", "r11", "xmm0", "xmm1", "xmm2", - "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", "xmm8", "xmm9", "xmm10", - "xmm11", "xmm12", "xmm13", "xmm14", "xmm15", "flags", "fpsr", "st", - "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)"); - - ASSERT_EQ(ret, kTestReturnValue); -} - -} // namespace partition_alloc::internal - -#endif // BUILDFLAG(ENABLE_PKEYS)
diff --git a/base/allocator/partition_allocator/pointers/raw_ptr.h b/base/allocator/partition_allocator/pointers/raw_ptr.h deleted file mode 100644 index 1b83201..0000000 --- a/base/allocator/partition_allocator/pointers/raw_ptr.h +++ /dev/null
@@ -1,1239 +0,0 @@ -// 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 BASE_ALLOCATOR_PARTITION_ALLOCATOR_POINTERS_RAW_PTR_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_POINTERS_RAW_PTR_H_ - -#include <stddef.h> -#include <stdint.h> - -#include <climits> -#include <cstddef> -#include <functional> -#include <type_traits> -#include <utility> - -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_base/cxx20_is_constant_evaluated.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_config.h" -#include "base/allocator/partition_allocator/partition_alloc_forward.h" -#include "base/allocator/partition_allocator/pointers/raw_ptr_exclusion.h" -#include "build/build_config.h" -#include "build/buildflag.h" - -#if BUILDFLAG(IS_WIN) -#include "base/allocator/partition_allocator/partition_alloc_base/win/win_handle_types.h" -#endif - -#if BUILDFLAG(USE_PARTITION_ALLOC) -#include "base/allocator/partition_allocator/partition_alloc_base/check.h" -// Live implementation of MiraclePtr being built. -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) || \ - BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) -#define PA_RAW_PTR_CHECK(condition) PA_BASE_CHECK(condition) -#else -// No-op implementation of MiraclePtr being built. -// Note that `PA_BASE_DCHECK()` evaporates from non-DCHECK builds, -// minimizing impact of generated code. -#define PA_RAW_PTR_CHECK(condition) PA_BASE_DCHECK(condition) -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) || - // BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) -#else // BUILDFLAG(USE_PARTITION_ALLOC) -// Without PartitionAlloc, there's no `PA_BASE_D?CHECK()` implementation -// available. -#define PA_RAW_PTR_CHECK(condition) -#endif // BUILDFLAG(USE_PARTITION_ALLOC) - -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) -#include "base/allocator/partition_allocator/pointers/raw_ptr_backup_ref_impl.h" -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - -#if BUILDFLAG(USE_ASAN_UNOWNED_PTR) -#include "base/allocator/partition_allocator/pointers/raw_ptr_asan_unowned_impl.h" -#endif // BUILDFLAG(USE_ASAN_UNOWNED_PTR) - -#if BUILDFLAG(USE_HOOKABLE_RAW_PTR) -#include "base/allocator/partition_allocator/pointers/raw_ptr_hookable_impl.h" -#endif // BUILDFLAG(USE_HOOKABLE_RAW_PTR) - -namespace cc { -class Scheduler; -} -namespace base::internal { -class DelayTimerBase; -} -namespace content::responsiveness { -class Calculator; -} - -namespace base { - -// NOTE: All methods should be `PA_ALWAYS_INLINE`. raw_ptr is meant to be a -// lightweight replacement of a raw pointer, hence performance is critical. - -// This is a bitfield representing the different flags that can be applied to a -// raw_ptr. -// -// Internal use only: Developers shouldn't use those values directly. -// -// Housekeeping rules: Try not to change trait values, so that numeric trait -// values stay constant across builds (could be useful e.g. when analyzing stack -// traces). A reasonable exception to this rule are `*ForTest` traits. As a -// matter of fact, we propose that new non-test traits are added before the -// `*ForTest` traits. -enum class RawPtrTraits : unsigned { - kEmpty = 0, - - // Disables dangling pointer detection, but keeps other raw_ptr protections. - // - // Don't use directly, use DisableDanglingPtrDetection or DanglingUntriaged - // instead. - kMayDangle = (1 << 0), - -#if BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) - // Disables any hooks, by switching to NoOpImpl in that case. - // - // Internal use only. - kDisableHooks = (1 << 2), -#else - kDisableHooks = kEmpty, -#endif - - // Pointer arithmetic is discouraged and disabled by default. - // - // Don't use directly, use AllowPtrArithmetic instead. - kAllowPtrArithmetic = (1 << 3), - - // Adds accounting, on top of the chosen implementation, for test purposes. - // raw_ptr/raw_ref with this trait perform extra bookkeeping, e.g. to track - // the number of times the raw_ptr is wrapped, unwrapped, etc. - // - // Test only. - kUseCountingWrapperForTest = (1 << 4), - - // Helper trait that can be used to test raw_ptr's behaviour or conversions. - // - // Test only. - kDummyForTest = (1 << 5), -}; - -// Used to combine RawPtrTraits: -constexpr RawPtrTraits operator|(RawPtrTraits a, RawPtrTraits b) { - return static_cast<RawPtrTraits>(static_cast<unsigned>(a) | - static_cast<unsigned>(b)); -} -constexpr RawPtrTraits operator&(RawPtrTraits a, RawPtrTraits b) { - return static_cast<RawPtrTraits>(static_cast<unsigned>(a) & - static_cast<unsigned>(b)); -} -constexpr RawPtrTraits operator~(RawPtrTraits a) { - return static_cast<RawPtrTraits>(~static_cast<unsigned>(a)); -} - -namespace raw_ptr_traits { - -constexpr bool Contains(RawPtrTraits a, RawPtrTraits b) { - return (a & b) != RawPtrTraits::kEmpty; -} - -constexpr RawPtrTraits Remove(RawPtrTraits a, RawPtrTraits b) { - return a & ~b; -} - -constexpr bool AreValid(RawPtrTraits traits) { - return Remove(traits, RawPtrTraits::kMayDangle | RawPtrTraits::kDisableHooks | - RawPtrTraits::kAllowPtrArithmetic | - RawPtrTraits::kUseCountingWrapperForTest | - RawPtrTraits::kDummyForTest) == - RawPtrTraits::kEmpty; -} - -template <RawPtrTraits Traits> -struct TraitsToImpl; - -} // namespace raw_ptr_traits - -template <typename T, RawPtrTraits Traits = RawPtrTraits::kEmpty> -class raw_ptr; - -#if BUILDFLAG(ENABLE_RAW_PTR_EXPERIMENTAL) -template <typename T, RawPtrTraits Traits = RawPtrTraits::kEmpty> -using raw_ptr_experimental = raw_ptr<T, Traits>; -#else -template <typename T, RawPtrTraits Traits = RawPtrTraits::kEmpty> -using raw_ptr_experimental = T*; -#endif // BUILDFLAG(ENABLE_RAW_PTR_EXPERIMENTAL) - -} // namespace base - -// This type is to be used internally, or in callbacks arguments when it is -// known that they might receive dangling pointers. In any other cases, please -// use one of: -// - raw_ptr<T, DanglingUntriaged> -// - raw_ptr<T, DisableDanglingPtrDetection> -template <typename T, base::RawPtrTraits Traits = base::RawPtrTraits::kEmpty> -using MayBeDangling = base::raw_ptr<T, Traits | base::RawPtrTraits::kMayDangle>; - -namespace base { - -namespace internal { -// These classes/structures are part of the raw_ptr implementation. -// DO NOT USE THESE CLASSES DIRECTLY YOURSELF. - -struct RawPtrNoOpImpl { - static constexpr bool kMustZeroOnInit = false; - static constexpr bool kMustZeroOnMove = false; - static constexpr bool kMustZeroOnDestruct = false; - - // Wraps a pointer. - template <typename T> - PA_ALWAYS_INLINE static constexpr T* WrapRawPtr(T* ptr) { - return ptr; - } - - // Notifies the allocator when a wrapped pointer is being removed or replaced. - template <typename T> - PA_ALWAYS_INLINE static constexpr void ReleaseWrappedPtr(T*) {} - - // Unwraps the pointer, while asserting that memory hasn't been freed. The - // function is allowed to crash on nullptr. - template <typename T> - PA_ALWAYS_INLINE static constexpr T* SafelyUnwrapPtrForDereference( - T* wrapped_ptr) { - return wrapped_ptr; - } - - // Unwraps the pointer, while asserting that memory hasn't been freed. The - // function must handle nullptr gracefully. - template <typename T> - PA_ALWAYS_INLINE static constexpr T* SafelyUnwrapPtrForExtraction( - T* wrapped_ptr) { - return wrapped_ptr; - } - - // Unwraps the pointer, without making an assertion on whether memory was - // freed or not. - template <typename T> - PA_ALWAYS_INLINE static constexpr T* UnsafelyUnwrapPtrForComparison( - T* wrapped_ptr) { - return wrapped_ptr; - } - - // Upcasts the wrapped pointer. - template <typename To, typename From> - PA_ALWAYS_INLINE static constexpr To* Upcast(From* wrapped_ptr) { - static_assert(std::is_convertible<From*, To*>::value, - "From must be convertible to To."); - // Note, this cast may change the address if upcasting to base that lies in - // the middle of the derived object. - return wrapped_ptr; - } - - // Advance the wrapped pointer by `delta_elems`. - template < - typename T, - typename Z, - typename = - std::enable_if_t<partition_alloc::internal::is_offset_type<Z>, void>> - PA_ALWAYS_INLINE static constexpr T* Advance(T* wrapped_ptr, Z delta_elems) { - return wrapped_ptr + delta_elems; - } - - // Retreat the wrapped pointer by `delta_elems`. - template < - typename T, - typename Z, - typename = - std::enable_if_t<partition_alloc::internal::is_offset_type<Z>, void>> - PA_ALWAYS_INLINE static constexpr T* Retreat(T* wrapped_ptr, Z delta_elems) { - return wrapped_ptr - delta_elems; - } - - template <typename T> - PA_ALWAYS_INLINE static constexpr ptrdiff_t GetDeltaElems(T* wrapped_ptr1, - T* wrapped_ptr2) { - return wrapped_ptr1 - wrapped_ptr2; - } - - // Returns a copy of a wrapped pointer, without making an assertion on whether - // memory was freed or not. - template <typename T> - PA_ALWAYS_INLINE static constexpr T* Duplicate(T* wrapped_ptr) { - return wrapped_ptr; - } - - // `WrapRawPtrForDuplication` and `UnsafelyUnwrapPtrForDuplication` are used - // to create a new raw_ptr<T> from another raw_ptr<T> of a different flavor. - template <typename T> - PA_ALWAYS_INLINE static constexpr T* WrapRawPtrForDuplication(T* ptr) { - return ptr; - } - - template <typename T> - PA_ALWAYS_INLINE static constexpr T* UnsafelyUnwrapPtrForDuplication( - T* wrapped_ptr) { - return wrapped_ptr; - } - - // This is for accounting only, used by unit tests. - PA_ALWAYS_INLINE constexpr static void IncrementSwapCountForTest() {} - PA_ALWAYS_INLINE constexpr static void IncrementLessCountForTest() {} - PA_ALWAYS_INLINE constexpr static void - IncrementPointerToMemberOperatorCountForTest() {} -}; - -// Wraps a raw_ptr/raw_ref implementation, with a class of the same interface -// that provides accounting, for test purposes. raw_ptr/raw_ref that use it -// perform extra bookkeeping, e.g. to track the number of times the raw_ptr is -// wrapped, unrwapped, etc. -// -// Test only. -template <RawPtrTraits Traits> -struct RawPtrCountingImplWrapperForTest - : public raw_ptr_traits::TraitsToImpl<Traits>::Impl { - static_assert( - !raw_ptr_traits::Contains(Traits, - RawPtrTraits::kUseCountingWrapperForTest)); - - using SuperImpl = typename raw_ptr_traits::TraitsToImpl<Traits>::Impl; - - static constexpr bool kMustZeroOnInit = SuperImpl::kMustZeroOnInit; - static constexpr bool kMustZeroOnMove = SuperImpl::kMustZeroOnMove; - static constexpr bool kMustZeroOnDestruct = SuperImpl::kMustZeroOnDestruct; - - template <typename T> - PA_ALWAYS_INLINE static constexpr T* WrapRawPtr(T* ptr) { - ++wrap_raw_ptr_cnt; - return SuperImpl::WrapRawPtr(ptr); - } - - template <typename T> - PA_ALWAYS_INLINE static constexpr void ReleaseWrappedPtr(T* ptr) { - ++release_wrapped_ptr_cnt; - SuperImpl::ReleaseWrappedPtr(ptr); - } - - template <typename T> - PA_ALWAYS_INLINE static constexpr T* SafelyUnwrapPtrForDereference( - T* wrapped_ptr) { - ++get_for_dereference_cnt; - return SuperImpl::SafelyUnwrapPtrForDereference(wrapped_ptr); - } - - template <typename T> - PA_ALWAYS_INLINE static constexpr T* SafelyUnwrapPtrForExtraction( - T* wrapped_ptr) { - ++get_for_extraction_cnt; - return SuperImpl::SafelyUnwrapPtrForExtraction(wrapped_ptr); - } - - template <typename T> - PA_ALWAYS_INLINE static constexpr T* UnsafelyUnwrapPtrForComparison( - T* wrapped_ptr) { - ++get_for_comparison_cnt; - return SuperImpl::UnsafelyUnwrapPtrForComparison(wrapped_ptr); - } - - PA_ALWAYS_INLINE static constexpr void IncrementSwapCountForTest() { - ++wrapped_ptr_swap_cnt; - } - - PA_ALWAYS_INLINE static constexpr void IncrementLessCountForTest() { - ++wrapped_ptr_less_cnt; - } - - PA_ALWAYS_INLINE static constexpr void - IncrementPointerToMemberOperatorCountForTest() { - ++pointer_to_member_operator_cnt; - } - - template <typename T> - PA_ALWAYS_INLINE static constexpr T* WrapRawPtrForDuplication(T* ptr) { - ++wrap_raw_ptr_for_dup_cnt; - return SuperImpl::WrapRawPtrForDuplication(ptr); - } - - template <typename T> - PA_ALWAYS_INLINE static constexpr T* UnsafelyUnwrapPtrForDuplication( - T* wrapped_ptr) { - ++get_for_duplication_cnt; - return SuperImpl::UnsafelyUnwrapPtrForDuplication(wrapped_ptr); - } - - static constexpr void ClearCounters() { - wrap_raw_ptr_cnt = 0; - release_wrapped_ptr_cnt = 0; - get_for_dereference_cnt = 0; - get_for_extraction_cnt = 0; - get_for_comparison_cnt = 0; - wrapped_ptr_swap_cnt = 0; - wrapped_ptr_less_cnt = 0; - pointer_to_member_operator_cnt = 0; - wrap_raw_ptr_for_dup_cnt = 0; - get_for_duplication_cnt = 0; - } - - static inline int wrap_raw_ptr_cnt = INT_MIN; - static inline int release_wrapped_ptr_cnt = INT_MIN; - static inline int get_for_dereference_cnt = INT_MIN; - static inline int get_for_extraction_cnt = INT_MIN; - static inline int get_for_comparison_cnt = INT_MIN; - static inline int wrapped_ptr_swap_cnt = INT_MIN; - static inline int wrapped_ptr_less_cnt = INT_MIN; - static inline int pointer_to_member_operator_cnt = INT_MIN; - static inline int wrap_raw_ptr_for_dup_cnt = INT_MIN; - static inline int get_for_duplication_cnt = INT_MIN; -}; - -} // namespace internal - -namespace raw_ptr_traits { - -// IsSupportedType<T>::value answers whether raw_ptr<T> 1) compiles and 2) is -// always safe at runtime. Templates that may end up using `raw_ptr<T>` should -// use IsSupportedType to ensure that raw_ptr is not used with unsupported -// types. As an example, see how base::internal::StorageTraits uses -// IsSupportedType as a condition for using base::internal::UnretainedWrapper -// (which has a `ptr_` field that will become `raw_ptr<T>` after the Big -// Rewrite). -template <typename T, typename SFINAE = void> -struct IsSupportedType { - static constexpr bool value = true; -}; - -// raw_ptr<T> is not compatible with function pointer types. Also, they don't -// even need the raw_ptr protection, because they don't point on heap. -template <typename T> -struct IsSupportedType<T, std::enable_if_t<std::is_function<T>::value>> { - static constexpr bool value = false; -}; - -// This section excludes some types from raw_ptr<T> to avoid them from being -// used inside base::Unretained in performance sensitive places. These were -// identified from sampling profiler data. See crbug.com/1287151 for more info. -template <> -struct IsSupportedType<cc::Scheduler> { - static constexpr bool value = false; -}; -template <> -struct IsSupportedType<base::internal::DelayTimerBase> { - static constexpr bool value = false; -}; -template <> -struct IsSupportedType<content::responsiveness::Calculator> { - static constexpr bool value = false; -}; - -#if __OBJC__ -// raw_ptr<T> is not compatible with pointers to Objective-C classes for a -// multitude of reasons. They may fail to compile in many cases, and wouldn't -// work well with tagged pointers. Anyway, Objective-C objects have their own -// way of tracking lifespan, hence don't need the raw_ptr protection as much. -// -// Such pointers are detected by checking if they're convertible to |id| type. -template <typename T> -struct IsSupportedType<T, - std::enable_if_t<std::is_convertible<T*, id>::value>> { - static constexpr bool value = false; -}; -#endif // __OBJC__ - -#if BUILDFLAG(IS_WIN) -// raw_ptr<HWND__> is unsafe at runtime - if the handle happens to also -// represent a valid pointer into a PartitionAlloc-managed region then it can -// lead to manipulating random memory when treating it as BackupRefPtr -// ref-count. See also https://crbug.com/1262017. -// -// TODO(https://crbug.com/1262017): Cover other handle types like HANDLE, -// HLOCAL, HINTERNET, or HDEVINFO. Maybe we should avoid using raw_ptr<T> when -// T=void (as is the case in these handle types). OTOH, explicit, -// non-template-based raw_ptr<void> should be allowed. Maybe this can be solved -// by having 2 traits: IsPointeeAlwaysSafe (to be used in templates) and -// IsPointeeUsuallySafe (to be used in the static_assert in raw_ptr). The -// upside of this approach is that it will safely handle base::Bind closing over -// HANDLE. The downside of this approach is that base::Bind closing over a -// void* pointer will not get UaF protection. -#define PA_WINDOWS_HANDLE_TYPE(name) \ - template <> \ - struct IsSupportedType<name##__, void> { \ - static constexpr bool value = false; \ - }; -#include "base/allocator/partition_allocator/partition_alloc_base/win/win_handle_types_list.inc" -#undef PA_WINDOWS_HANDLE_TYPE -#endif - -template <RawPtrTraits Traits> -struct TraitsToImpl { - static_assert(AreValid(Traits), "Unknown raw_ptr trait(s)"); - - private: - // UnderlyingImpl is the struct that provides the implementation of the - // protections related to raw_ptr. -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - using UnderlyingImpl = internal::RawPtrBackupRefImpl< - /*allow_dangling=*/Contains(Traits, RawPtrTraits::kMayDangle)>; - -#elif BUILDFLAG(USE_ASAN_UNOWNED_PTR) - using UnderlyingImpl = std::conditional_t< - Contains(Traits, RawPtrTraits::kMayDangle), - // No special bookkeeping required for this case, - // just treat these as ordinary pointers. - internal::RawPtrNoOpImpl, - internal::RawPtrAsanUnownedImpl< - Contains(Traits, RawPtrTraits::kAllowPtrArithmetic)>>; -#elif BUILDFLAG(USE_HOOKABLE_RAW_PTR) - using UnderlyingImpl = - std::conditional_t<Contains(Traits, RawPtrTraits::kDisableHooks), - internal::RawPtrNoOpImpl, - internal::RawPtrHookableImpl>; -#else - using UnderlyingImpl = internal::RawPtrNoOpImpl; -#endif - - public: - // Impl is the struct that implements raw_ptr functions. Think of raw_ptr as a - // thin wrapper, that directs calls to Impl. - // Impl may be different from UnderlyingImpl, because it may include a - // wrapper. - using Impl = std::conditional_t< - Contains(Traits, RawPtrTraits::kUseCountingWrapperForTest), - internal::RawPtrCountingImplWrapperForTest< - Remove(Traits, RawPtrTraits::kUseCountingWrapperForTest)>, - UnderlyingImpl>; -}; - -} // namespace raw_ptr_traits - -// `raw_ptr<T>` is a non-owning smart pointer that has improved memory-safety -// over raw pointers. It behaves just like a raw pointer on platforms where -// USE_BACKUP_REF_PTR is off, and almost like one when it's on (the main -// difference is that it's zero-initialized and cleared on destruction and -// move). Unlike `std::unique_ptr<T>`, `base::scoped_refptr<T>`, etc., it -// doesn’t manage ownership or lifetime of an allocated object - you are still -// responsible for freeing the object when no longer used, just as you would -// with a raw C++ pointer. -// -// Compared to a raw C++ pointer, on platforms where USE_BACKUP_REF_PTR is on, -// `raw_ptr<T>` incurs additional performance overhead for initialization, -// destruction, and assignment (including `ptr++` and `ptr += ...`). There is -// no overhead when dereferencing a pointer. -// -// `raw_ptr<T>` is beneficial for security, because it can prevent a significant -// percentage of Use-after-Free (UaF) bugs from being exploitable. `raw_ptr<T>` -// has limited impact on stability - dereferencing a dangling pointer remains -// Undefined Behavior. Note that the security protection is not yet enabled by -// default. -// -// raw_ptr<T> is marked as [[gsl::Pointer]] which allows the compiler to catch -// some bugs where the raw_ptr holds a dangling pointer to a temporary object. -// However the [[gsl::Pointer]] analysis expects that such types do not have a -// non-default move constructor/assignment. Thus, it's possible to get an error -// where the pointer is not actually dangling, and have to work around the -// compiler. We have not managed to construct such an example in Chromium yet. -template <typename T, RawPtrTraits Traits> -class PA_TRIVIAL_ABI PA_GSL_POINTER raw_ptr { - public: - using Impl = typename raw_ptr_traits::TraitsToImpl<Traits>::Impl; - -#if !BUILDFLAG(USE_PARTITION_ALLOC) - // See comment at top about `PA_RAW_PTR_CHECK()`. - static_assert(std::is_same_v<Impl, internal::RawPtrNoOpImpl>); -#endif // !BUILDFLAG(USE_PARTITION_ALLOC) - - static_assert(raw_ptr_traits::IsSupportedType<T>::value, - "raw_ptr<T> doesn't work with this kind of pointee type T"); - - // TODO(bartekn): Turn on zeroing as much as possible, to reduce - // pointer-related UBs. In the current implementation we do it only when the - // underlying implementation needs it for correctness, for performance - // reasons. There are two secnarios where it's important: - // 1. When rewriting renderer, we don't want extra overhead get in the way of - // our perf evaluation. - // 2. The same applies to rewriting 3rd party libraries, but also we want - // RawPtrNoOpImpl to be a true no-op, in case the library is linked with - // a product other than Chromium (this can be mitigated using - // `build_with_chromium` GN variable). - static constexpr bool kZeroOnInit = Impl::kMustZeroOnInit; - static constexpr bool kZeroOnMove = Impl::kMustZeroOnMove; - static constexpr bool kZeroOnDestruct = Impl::kMustZeroOnDestruct; - -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) || \ - BUILDFLAG(USE_ASAN_UNOWNED_PTR) || BUILDFLAG(USE_HOOKABLE_RAW_PTR) - // BackupRefPtr requires a non-trivial default constructor, destructor, etc. - PA_ALWAYS_INLINE constexpr raw_ptr() noexcept { - if constexpr (kZeroOnInit) { - wrapped_ptr_ = nullptr; - } - } - - PA_ALWAYS_INLINE constexpr raw_ptr(const raw_ptr& p) noexcept - : wrapped_ptr_(Impl::Duplicate(p.wrapped_ptr_)) {} - - PA_ALWAYS_INLINE constexpr raw_ptr(raw_ptr&& p) noexcept { - wrapped_ptr_ = p.wrapped_ptr_; - if constexpr (kZeroOnMove) { - p.wrapped_ptr_ = nullptr; - } - } - - PA_ALWAYS_INLINE constexpr raw_ptr& operator=(const raw_ptr& p) noexcept { - // Duplicate before releasing, in case the pointer is assigned to itself. - // - // Unlike the move version of this operator, don't add |this != &p| branch, - // for performance reasons. Even though Duplicate() is not cheap, we - // practically never assign a raw_ptr<T> to itself. We suspect that a - // cumulative cost of a conditional branch, even if always correctly - // predicted, would exceed that. - T* new_ptr = Impl::Duplicate(p.wrapped_ptr_); - Impl::ReleaseWrappedPtr(wrapped_ptr_); - wrapped_ptr_ = new_ptr; - return *this; - } - - PA_ALWAYS_INLINE constexpr raw_ptr& operator=(raw_ptr&& p) noexcept { - // Unlike the the copy version of this operator, this branch is necessaty - // for correctness. - if (PA_LIKELY(this != &p)) { - Impl::ReleaseWrappedPtr(wrapped_ptr_); - wrapped_ptr_ = p.wrapped_ptr_; - if constexpr (kZeroOnMove) { - p.wrapped_ptr_ = nullptr; - } - } - return *this; - } - -// Constexpr destructors were introduced in C++20. PartitionAlloc's minimum -// supported C++ version is C++17. -#if defined(__cpp_constexpr) && __cpp_constexpr >= 201907L - PA_ALWAYS_INLINE constexpr ~raw_ptr() noexcept { -#else - PA_ALWAYS_INLINE ~raw_ptr() noexcept { -#endif - Impl::ReleaseWrappedPtr(wrapped_ptr_); - // Work around external issues where raw_ptr is used after destruction. - if constexpr (kZeroOnDestruct) { - wrapped_ptr_ = nullptr; - } - } - -#else // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) || - // BUILDFLAG(USE_ASAN_UNOWNED_PTR) || BUILDFLAG(USE_HOOKABLE_RAW_PTR) - - // raw_ptr can be trivially default constructed (leaving |wrapped_ptr_| - // uninitialized). - PA_ALWAYS_INLINE constexpr raw_ptr() noexcept = default; - - // In addition to nullptr_t ctor above, raw_ptr needs to have these - // as |=default| or |constexpr| to avoid hitting -Wglobal-constructors in - // cases like this: - // struct SomeStruct { int int_field; raw_ptr<int> ptr_field; }; - // SomeStruct g_global_var = { 123, nullptr }; - PA_ALWAYS_INLINE raw_ptr(const raw_ptr&) noexcept = default; - PA_ALWAYS_INLINE raw_ptr(raw_ptr&&) noexcept = default; - PA_ALWAYS_INLINE raw_ptr& operator=(const raw_ptr&) noexcept = default; - PA_ALWAYS_INLINE raw_ptr& operator=(raw_ptr&&) noexcept = default; - - PA_ALWAYS_INLINE ~raw_ptr() noexcept = default; - - // With default constructor, destructor and move operations, we don't have an - // opportunity to zero the underlying pointer, so ensure this isn't expected. - static_assert(!kZeroOnInit); - static_assert(!kZeroOnMove); - static_assert(!kZeroOnDestruct); -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) || - // BUILDFLAG(USE_ASAN_UNOWNED_PTR) - - // Cross-kind copy constructor. - // Move is not supported as different traits may use different ref-counts, so - // let move operations degrade to copy, which handles it well. - template <RawPtrTraits PassedTraits, - typename Unused = std::enable_if_t<Traits != PassedTraits>> - PA_ALWAYS_INLINE constexpr explicit raw_ptr( - const raw_ptr<T, PassedTraits>& p) noexcept - : wrapped_ptr_(Impl::WrapRawPtrForDuplication( - raw_ptr_traits::TraitsToImpl<PassedTraits>::Impl:: - UnsafelyUnwrapPtrForDuplication(p.wrapped_ptr_))) { - // Limit cross-kind conversions only to cases where kMayDangle gets added, - // because that's needed for Unretained(Ref)Wrapper. Use a static_assert, - // instead of disabling via SFINAE, so that the compiler catches other - // conversions. Otherwise implicit raw_ptr<T> -> T* -> raw_ptr<> route will - // be taken. - static_assert(Traits == (PassedTraits | RawPtrTraits::kMayDangle)); - } - - // Cross-kind assignment. - // Move is not supported as different traits may use different ref-counts, so - // let move operations degrade to copy, which handles it well. - template <RawPtrTraits PassedTraits, - typename Unused = std::enable_if_t<Traits != PassedTraits>> - PA_ALWAYS_INLINE constexpr raw_ptr& operator=( - const raw_ptr<T, PassedTraits>& p) noexcept { - // Limit cross-kind assignments only to cases where kMayDangle gets added, - // because that's needed for Unretained(Ref)Wrapper. Use a static_assert, - // instead of disabling via SFINAE, so that the compiler catches other - // conversions. Otherwise implicit raw_ptr<T> -> T* -> raw_ptr<> route will - // be taken. - static_assert(Traits == (PassedTraits | RawPtrTraits::kMayDangle)); - - Impl::ReleaseWrappedPtr(wrapped_ptr_); - wrapped_ptr_ = Impl::WrapRawPtrForDuplication( - raw_ptr_traits::TraitsToImpl<PassedTraits>::Impl:: - UnsafelyUnwrapPtrForDuplication(p.wrapped_ptr_)); - return *this; - } - - // Deliberately implicit, because raw_ptr is supposed to resemble raw ptr. - // Ignore kZeroOnInit, because here the caller explicitly wishes to initialize - // with nullptr. NOLINTNEXTLINE(google-explicit-constructor) - PA_ALWAYS_INLINE constexpr raw_ptr(std::nullptr_t) noexcept - : wrapped_ptr_(nullptr) {} - - // Deliberately implicit, because raw_ptr is supposed to resemble raw ptr. - // NOLINTNEXTLINE(google-explicit-constructor) - PA_ALWAYS_INLINE constexpr raw_ptr(T* p) noexcept - : wrapped_ptr_(Impl::WrapRawPtr(p)) {} - - // Deliberately implicit in order to support implicit upcast. - template <typename U, - typename Unused = std::enable_if_t< - std::is_convertible<U*, T*>::value && - !std::is_void<typename std::remove_cv<T>::type>::value>> - // NOLINTNEXTLINE(google-explicit-constructor) - PA_ALWAYS_INLINE constexpr raw_ptr(const raw_ptr<U, Traits>& ptr) noexcept - : wrapped_ptr_( - Impl::Duplicate(Impl::template Upcast<T, U>(ptr.wrapped_ptr_))) {} - // Deliberately implicit in order to support implicit upcast. - template <typename U, - typename Unused = std::enable_if_t< - std::is_convertible<U*, T*>::value && - !std::is_void<typename std::remove_cv<T>::type>::value>> - // NOLINTNEXTLINE(google-explicit-constructor) - PA_ALWAYS_INLINE constexpr raw_ptr(raw_ptr<U, Traits>&& ptr) noexcept - : wrapped_ptr_(Impl::template Upcast<T, U>(ptr.wrapped_ptr_)) { - if constexpr (kZeroOnMove) { - ptr.wrapped_ptr_ = nullptr; - } - } - - PA_ALWAYS_INLINE constexpr raw_ptr& operator=(std::nullptr_t) noexcept { - Impl::ReleaseWrappedPtr(wrapped_ptr_); - wrapped_ptr_ = nullptr; - return *this; - } - PA_ALWAYS_INLINE constexpr raw_ptr& operator=(T* p) noexcept { - Impl::ReleaseWrappedPtr(wrapped_ptr_); - wrapped_ptr_ = Impl::WrapRawPtr(p); - return *this; - } - - // Upcast assignment - template <typename U, - typename Unused = std::enable_if_t< - std::is_convertible<U*, T*>::value && - !std::is_void<typename std::remove_cv<T>::type>::value>> - PA_ALWAYS_INLINE constexpr raw_ptr& operator=( - const raw_ptr<U, Traits>& ptr) noexcept { - // Make sure that pointer isn't assigned to itself (look at raw_ptr address, - // not its contained pointer value). The comparison is only needed when they - // are the same type, otherwise they can't be the same raw_ptr object. -#if BUILDFLAG(PA_DCHECK_IS_ON) || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) - if constexpr (std::is_same_v<raw_ptr, std::decay_t<decltype(ptr)>>) { - PA_RAW_PTR_CHECK(this != &ptr); - } -#endif - Impl::ReleaseWrappedPtr(wrapped_ptr_); - wrapped_ptr_ = - Impl::Duplicate(Impl::template Upcast<T, U>(ptr.wrapped_ptr_)); - return *this; - } - template <typename U, - typename Unused = std::enable_if_t< - std::is_convertible<U*, T*>::value && - !std::is_void<typename std::remove_cv<T>::type>::value>> - PA_ALWAYS_INLINE constexpr raw_ptr& operator=( - raw_ptr<U, Traits>&& ptr) noexcept { - // Make sure that pointer isn't assigned to itself (look at raw_ptr address, - // not its contained pointer value). The comparison is only needed when they - // are the same type, otherwise they can't be the same raw_ptr object. -#if BUILDFLAG(PA_DCHECK_IS_ON) || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) - if constexpr (std::is_same_v<raw_ptr, std::decay_t<decltype(ptr)>>) { - PA_RAW_PTR_CHECK(this != &ptr); - } -#endif - Impl::ReleaseWrappedPtr(wrapped_ptr_); - wrapped_ptr_ = Impl::template Upcast<T, U>(ptr.wrapped_ptr_); - if constexpr (kZeroOnMove) { - ptr.wrapped_ptr_ = nullptr; - } - return *this; - } - - // Avoid using. The goal of raw_ptr is to be as close to raw pointer as - // possible, so use it only if absolutely necessary (e.g. for const_cast). - PA_ALWAYS_INLINE constexpr T* get() const { return GetForExtraction(); } - - PA_ALWAYS_INLINE constexpr explicit operator bool() const { - return !!wrapped_ptr_; - } - - template <typename U = T, - typename Unused = std::enable_if_t< - !std::is_void<typename std::remove_cv<U>::type>::value>> - PA_ALWAYS_INLINE constexpr U& operator*() const { - return *GetForDereference(); - } - PA_ALWAYS_INLINE constexpr T* operator->() const { - return GetForDereference(); - } - - // Disables `(my_raw_ptr->*pmf)(...)` as a workaround for - // the ICE in GCC parsing the code, reported at - // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103455 - template <typename PMF> - void operator->*(PMF) const = delete; - - // Deliberately implicit, because raw_ptr is supposed to resemble raw ptr. - // NOLINTNEXTLINE(google-explicit-constructor) - PA_ALWAYS_INLINE constexpr operator T*() const { return GetForExtraction(); } - template <typename U> - PA_ALWAYS_INLINE constexpr explicit operator U*() const { - // This operator may be invoked from static_cast, meaning the types may not - // be implicitly convertible, hence the need for static_cast here. - return static_cast<U*>(GetForExtraction()); - } - - PA_ALWAYS_INLINE constexpr raw_ptr& operator++() { - wrapped_ptr_ = Impl::Advance(wrapped_ptr_, 1); - return *this; - } - PA_ALWAYS_INLINE constexpr raw_ptr& operator--() { - wrapped_ptr_ = Impl::Retreat(wrapped_ptr_, 1); - return *this; - } - PA_ALWAYS_INLINE constexpr raw_ptr operator++(int /* post_increment */) { - raw_ptr result = *this; - ++(*this); - return result; - } - PA_ALWAYS_INLINE constexpr raw_ptr operator--(int /* post_decrement */) { - raw_ptr result = *this; - --(*this); - return result; - } - template < - typename Z, - typename = std::enable_if_t<partition_alloc::internal::is_offset_type<Z>>> - PA_ALWAYS_INLINE constexpr raw_ptr& operator+=(Z delta_elems) { - wrapped_ptr_ = Impl::Advance(wrapped_ptr_, delta_elems); - return *this; - } - template < - typename Z, - typename = std::enable_if_t<partition_alloc::internal::is_offset_type<Z>>> - PA_ALWAYS_INLINE constexpr raw_ptr& operator-=(Z delta_elems) { - wrapped_ptr_ = Impl::Retreat(wrapped_ptr_, delta_elems); - return *this; - } - - // Do not disable operator+() and operator-(). - // They provide OOB checks, which prevent from assigning an arbitrary value to - // raw_ptr, leading BRP to modifying arbitrary memory thinking it's ref-count. - // Keep them enabled, which may be blocked later when attempting to apply the - // += or -= operation, when disabled. In the absence of operators +/-, the - // compiler is free to implicitly convert to the underlying T* representation - // and perform ordinary pointer arithmetic, thus invalidating the purpose - // behind disabling them. - template <typename Z> - PA_ALWAYS_INLINE friend constexpr raw_ptr operator+(const raw_ptr& p, - Z delta_elems) { - raw_ptr result = p; - return result += delta_elems; - } - template <typename Z> - PA_ALWAYS_INLINE friend constexpr raw_ptr operator-(const raw_ptr& p, - Z delta_elems) { - raw_ptr result = p; - return result -= delta_elems; - } - - PA_ALWAYS_INLINE friend constexpr ptrdiff_t operator-(const raw_ptr& p1, - const raw_ptr& p2) { - return Impl::GetDeltaElems(p1.wrapped_ptr_, p2.wrapped_ptr_); - } - PA_ALWAYS_INLINE friend constexpr ptrdiff_t operator-(T* p1, - const raw_ptr& p2) { - return Impl::GetDeltaElems(p1, p2.wrapped_ptr_); - } - PA_ALWAYS_INLINE friend constexpr ptrdiff_t operator-(const raw_ptr& p1, - T* p2) { - return Impl::GetDeltaElems(p1.wrapped_ptr_, p2); - } - - // Stop referencing the underlying pointer and free its memory. Compared to - // raw delete calls, this avoids the raw_ptr to be temporarily dangling - // during the free operation, which will lead to taking the slower path that - // involves quarantine. - PA_ALWAYS_INLINE constexpr void ClearAndDelete() noexcept { - delete GetForExtractionAndReset(); - } - PA_ALWAYS_INLINE constexpr void ClearAndDeleteArray() noexcept { - delete[] GetForExtractionAndReset(); - } - - // Clear the underlying pointer and return another raw_ptr instance - // that is allowed to dangle. - // This can be useful in cases such as: - // ``` - // ptr.ExtractAsDangling()->SelfDestroy(); - // ``` - // ``` - // c_style_api_do_something_and_destroy(ptr.ExtractAsDangling()); - // ``` - // NOTE, avoid using this method as it indicates an error-prone memory - // ownership pattern. If possible, use smart pointers like std::unique_ptr<> - // instead of raw_ptr<>. - // If you have to use it, avoid saving the return value in a long-lived - // variable (or worse, a field)! It's meant to be used as a temporary, to be - // passed into a cleanup & freeing function, and destructed at the end of the - // statement. - PA_ALWAYS_INLINE constexpr MayBeDangling<T, Traits> - ExtractAsDangling() noexcept { - MayBeDangling<T, Traits> res(std::move(*this)); - // Not all implementation clear the source pointer on move. Furthermore, - // even for implemtantions that do, cross-kind conversions (that add - // kMayDangle) fall back to a copy, instead of move. So do it here just in - // case. Should be cheap. - operator=(nullptr); - return res; - } - - // Comparison operators between raw_ptr and raw_ptr<U>/U*/std::nullptr_t. - // Strictly speaking, it is not necessary to provide these: the compiler can - // use the conversion operator implicitly to allow comparisons to fall back to - // comparisons between raw pointers. However, `operator T*`/`operator U*` may - // perform safety checks with a higher runtime cost, so to avoid this, provide - // explicit comparison operators for all combinations of parameters. - - // Comparisons between `raw_ptr`s. This unusual declaration and separate - // definition below is because `GetForComparison()` is a private method. The - // more conventional approach of defining a comparison operator between - // `raw_ptr` and `raw_ptr<U>` in the friend declaration itself does not work, - // because a comparison operator defined inline would not be allowed to call - // `raw_ptr<U>`'s private `GetForComparison()` method. - template <typename U, typename V, RawPtrTraits R1, RawPtrTraits R2> - friend bool operator==(const raw_ptr<U, R1>& lhs, const raw_ptr<V, R2>& rhs); - template <typename U, typename V, RawPtrTraits R1, RawPtrTraits R2> - friend bool operator!=(const raw_ptr<U, R1>& lhs, const raw_ptr<V, R2>& rhs); - template <typename U, typename V, RawPtrTraits R1, RawPtrTraits R2> - friend bool operator<(const raw_ptr<U, R1>& lhs, const raw_ptr<V, R2>& rhs); - template <typename U, typename V, RawPtrTraits R1, RawPtrTraits R2> - friend bool operator>(const raw_ptr<U, R1>& lhs, const raw_ptr<V, R2>& rhs); - template <typename U, typename V, RawPtrTraits R1, RawPtrTraits R2> - friend bool operator<=(const raw_ptr<U, R1>& lhs, const raw_ptr<V, R2>& rhs); - template <typename U, typename V, RawPtrTraits R1, RawPtrTraits R2> - friend bool operator>=(const raw_ptr<U, R1>& lhs, const raw_ptr<V, R2>& rhs); - - // Comparisons with U*. These operators also handle the case where the RHS is - // T*. - template <typename U> - PA_ALWAYS_INLINE friend bool operator==(const raw_ptr& lhs, U* rhs) { - return lhs.GetForComparison() == rhs; - } - template <typename U> - PA_ALWAYS_INLINE friend bool operator!=(const raw_ptr& lhs, U* rhs) { - return !(lhs == rhs); - } - template <typename U> - PA_ALWAYS_INLINE friend bool operator==(U* lhs, const raw_ptr& rhs) { - return rhs == lhs; // Reverse order to call the operator above. - } - template <typename U> - PA_ALWAYS_INLINE friend bool operator!=(U* lhs, const raw_ptr& rhs) { - return rhs != lhs; // Reverse order to call the operator above. - } - template <typename U> - PA_ALWAYS_INLINE friend bool operator<(const raw_ptr& lhs, U* rhs) { - return lhs.GetForComparison() < rhs; - } - template <typename U> - PA_ALWAYS_INLINE friend bool operator<=(const raw_ptr& lhs, U* rhs) { - return lhs.GetForComparison() <= rhs; - } - template <typename U> - PA_ALWAYS_INLINE friend bool operator>(const raw_ptr& lhs, U* rhs) { - return lhs.GetForComparison() > rhs; - } - template <typename U> - PA_ALWAYS_INLINE friend bool operator>=(const raw_ptr& lhs, U* rhs) { - return lhs.GetForComparison() >= rhs; - } - template <typename U> - PA_ALWAYS_INLINE friend bool operator<(U* lhs, const raw_ptr& rhs) { - return lhs < rhs.GetForComparison(); - } - template <typename U> - PA_ALWAYS_INLINE friend bool operator<=(U* lhs, const raw_ptr& rhs) { - return lhs <= rhs.GetForComparison(); - } - template <typename U> - PA_ALWAYS_INLINE friend bool operator>(U* lhs, const raw_ptr& rhs) { - return lhs > rhs.GetForComparison(); - } - template <typename U> - PA_ALWAYS_INLINE friend bool operator>=(U* lhs, const raw_ptr& rhs) { - return lhs >= rhs.GetForComparison(); - } - - // Comparisons with `std::nullptr_t`. - PA_ALWAYS_INLINE friend bool operator==(const raw_ptr& lhs, std::nullptr_t) { - return !lhs; - } - PA_ALWAYS_INLINE friend bool operator!=(const raw_ptr& lhs, std::nullptr_t) { - return !!lhs; // Use !! otherwise the costly implicit cast will be used. - } - PA_ALWAYS_INLINE friend bool operator==(std::nullptr_t, const raw_ptr& rhs) { - return !rhs; - } - PA_ALWAYS_INLINE friend bool operator!=(std::nullptr_t, const raw_ptr& rhs) { - return !!rhs; // Use !! otherwise the costly implicit cast will be used. - } - - PA_ALWAYS_INLINE friend constexpr void swap(raw_ptr& lhs, - raw_ptr& rhs) noexcept { - Impl::IncrementSwapCountForTest(); - std::swap(lhs.wrapped_ptr_, rhs.wrapped_ptr_); - } - - PA_ALWAYS_INLINE void ReportIfDangling() const noexcept { -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - Impl::ReportIfDangling(wrapped_ptr_); -#endif - } - - private: - // This getter is meant for situations where the pointer is meant to be - // dereferenced. It is allowed to crash on nullptr (it may or may not), - // because it knows that the caller will crash on nullptr. - PA_ALWAYS_INLINE constexpr T* GetForDereference() const { - return Impl::SafelyUnwrapPtrForDereference(wrapped_ptr_); - } - // This getter is meant for situations where the raw pointer is meant to be - // extracted outside of this class, but not necessarily with an intention to - // dereference. It mustn't crash on nullptr. - PA_ALWAYS_INLINE constexpr T* GetForExtraction() const { - return Impl::SafelyUnwrapPtrForExtraction(wrapped_ptr_); - } - // This getter is meant *only* for situations where the pointer is meant to be - // compared (guaranteeing no dereference or extraction outside of this class). - // Any verifications can and should be skipped for performance reasons. - PA_ALWAYS_INLINE constexpr T* GetForComparison() const { - return Impl::UnsafelyUnwrapPtrForComparison(wrapped_ptr_); - } - - PA_ALWAYS_INLINE constexpr T* GetForExtractionAndReset() { - T* ptr = GetForExtraction(); - operator=(nullptr); - return ptr; - } - - // This field is not a raw_ptr<> because it was filtered by the rewriter for: - // #union, #global-scope, #constexpr-ctor-field-initializer - RAW_PTR_EXCLUSION T* wrapped_ptr_; - - template <typename U, base::RawPtrTraits R> - friend class raw_ptr; -}; - -template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2> -PA_ALWAYS_INLINE bool operator==(const raw_ptr<U, Traits1>& lhs, - const raw_ptr<V, Traits2>& rhs) { - return lhs.GetForComparison() == rhs.GetForComparison(); -} - -template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2> -PA_ALWAYS_INLINE bool operator!=(const raw_ptr<U, Traits1>& lhs, - const raw_ptr<V, Traits2>& rhs) { - return !(lhs == rhs); -} - -template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2> -PA_ALWAYS_INLINE bool operator<(const raw_ptr<U, Traits1>& lhs, - const raw_ptr<V, Traits2>& rhs) { - return lhs.GetForComparison() < rhs.GetForComparison(); -} - -template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2> -PA_ALWAYS_INLINE bool operator>(const raw_ptr<U, Traits1>& lhs, - const raw_ptr<V, Traits2>& rhs) { - return lhs.GetForComparison() > rhs.GetForComparison(); -} - -template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2> -PA_ALWAYS_INLINE bool operator<=(const raw_ptr<U, Traits1>& lhs, - const raw_ptr<V, Traits2>& rhs) { - return lhs.GetForComparison() <= rhs.GetForComparison(); -} - -template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2> -PA_ALWAYS_INLINE bool operator>=(const raw_ptr<U, Traits1>& lhs, - const raw_ptr<V, Traits2>& rhs) { - return lhs.GetForComparison() >= rhs.GetForComparison(); -} - -template <typename T> -struct IsRawPtr : std::false_type {}; - -template <typename T, RawPtrTraits Traits> -struct IsRawPtr<raw_ptr<T, Traits>> : std::true_type {}; - -template <typename T> -inline constexpr bool IsRawPtrV = IsRawPtr<T>::value; - -template <typename T> -inline constexpr bool IsRawPtrMayDangleV = false; - -template <typename T, RawPtrTraits Traits> -inline constexpr bool IsRawPtrMayDangleV<raw_ptr<T, Traits>> = - raw_ptr_traits::Contains(Traits, RawPtrTraits::kMayDangle); - -// Template helpers for working with T* or raw_ptr<T>. -template <typename T> -struct IsPointer : std::false_type {}; - -template <typename T> -struct IsPointer<T*> : std::true_type {}; - -template <typename T, RawPtrTraits Traits> -struct IsPointer<raw_ptr<T, Traits>> : std::true_type {}; - -template <typename T> -inline constexpr bool IsPointerV = IsPointer<T>::value; - -template <typename T> -struct RemovePointer { - using type = T; -}; - -template <typename T> -struct RemovePointer<T*> { - using type = T; -}; - -template <typename T, RawPtrTraits Traits> -struct RemovePointer<raw_ptr<T, Traits>> { - using type = T; -}; - -template <typename T> -using RemovePointerT = typename RemovePointer<T>::type; - -} // namespace base - -using base::raw_ptr; -using base::raw_ptr_experimental; - -// DisableDanglingPtrDetection option for raw_ptr annotates -// "intentional-and-safe" dangling pointers. It is meant to be used at the -// margin, only if there is no better way to re-architecture the code. -// -// Usage: -// raw_ptr<T, DisableDanglingPtrDetection> dangling_ptr; -// -// When using it, please provide a justification about what guarantees that it -// will never be dereferenced after becoming dangling. -constexpr auto DisableDanglingPtrDetection = base::RawPtrTraits::kMayDangle; - -// See `docs/dangling_ptr.md` -// Annotates known dangling raw_ptr. Those haven't been triaged yet. All the -// occurrences are meant to be removed. See https://crbug.com/1291138. -constexpr auto DanglingUntriaged = base::RawPtrTraits::kMayDangle; - -// The use of pointer arithmetic with raw_ptr is strongly discouraged and -// disabled by default. Usually a container like span<> should be used -// instead of the raw_ptr. -constexpr auto AllowPtrArithmetic = base::RawPtrTraits::kAllowPtrArithmetic; - -// Temporary flag for `raw_ptr` / `raw_ref`. This is used by finch experiments -// to differentiate pointers added recently for the ChromeOS ash rewrite. -// -// See launch plan: -// https://docs.google.com/document/d/105OVhNl-2lrfWElQSk5BXYv-nLynfxUrbC4l8cZ0CoU/edit# -// -// This is not meant to be added manually. You can ignore this flag. -// -// TODO(https://crbug.com/1435441) Implement the ExperimentalAsh Trait. -constexpr auto ExperimentalAsh = base::RawPtrTraits::kMayDangle; - -namespace std { - -// Override so set/map lookups do not create extra raw_ptr. This also allows -// dangling pointers to be used for lookup. -template <typename T, base::RawPtrTraits Traits> -struct less<raw_ptr<T, Traits>> { - using Impl = typename raw_ptr<T, Traits>::Impl; - using is_transparent = void; - - bool operator()(const raw_ptr<T, Traits>& lhs, - const raw_ptr<T, Traits>& rhs) const { - Impl::IncrementLessCountForTest(); - return lhs < rhs; - } - - bool operator()(T* lhs, const raw_ptr<T, Traits>& rhs) const { - Impl::IncrementLessCountForTest(); - return lhs < rhs; - } - - bool operator()(const raw_ptr<T, Traits>& lhs, T* rhs) const { - Impl::IncrementLessCountForTest(); - return lhs < rhs; - } -}; - -// Define for cases where raw_ptr<T> holds a pointer to an array of type T. -// This is consistent with definition of std::iterator_traits<T*>. -// Algorithms like std::binary_search need that. -template <typename T, base::RawPtrTraits Traits> -struct iterator_traits<raw_ptr<T, Traits>> { - using difference_type = ptrdiff_t; - using value_type = std::remove_cv_t<T>; - using pointer = T*; - using reference = T&; - using iterator_category = std::random_access_iterator_tag; -}; - -#if defined(_LIBCPP_VERSION) -// Specialize std::pointer_traits. The latter is required to obtain the -// underlying raw pointer in the std::to_address(pointer) overload. -// Implementing the pointer_traits is the standard blessed way to customize -// `std::to_address(pointer)` in C++20 [3]. -// -// [1] https://wg21.link/pointer.traits.optmem - -template <typename T, ::base::RawPtrTraits Traits> -struct pointer_traits<::raw_ptr<T, Traits>> { - using pointer = ::raw_ptr<T, Traits>; - using element_type = T; - using difference_type = ptrdiff_t; - - template <typename U> - using rebind = ::raw_ptr<U, Traits>; - - static constexpr pointer pointer_to(element_type& r) noexcept { - return pointer(&r); - } - - static constexpr element_type* to_address(pointer p) noexcept { - return p.get(); - } -}; -#endif // defined(_LIBCPP_VERSION) - -} // namespace std - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_POINTERS_RAW_PTR_H_
diff --git a/base/allocator/partition_allocator/pointers/raw_ptr_asan_unowned_impl.cc b/base/allocator/partition_allocator/pointers/raw_ptr_asan_unowned_impl.cc deleted file mode 100644 index fb30593..0000000 --- a/base/allocator/partition_allocator/pointers/raw_ptr_asan_unowned_impl.cc +++ /dev/null
@@ -1,47 +0,0 @@ -// 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 "base/allocator/partition_allocator/pointers/raw_ptr_asan_unowned_impl.h" - -#include <sanitizer/asan_interface.h> -#include <cstdint> - -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" - -namespace base::internal { - -PA_NO_SANITIZE("address") -bool EndOfAliveAllocation(const volatile void* ptr, bool is_adjustable_ptr) { - uintptr_t address = reinterpret_cast<uintptr_t>(ptr); - - // Normally, we probe the first byte of an object, but in cases of pointer - // arithmetic, we may be probing subsequent bytes, including the legal - // "end + 1" position. - // - // Alas, ASAN will claim an unmapped page is unpoisoned, so willfully ignore - // the fist address of a page, since "end + 1" of an object allocated exactly - // up to a page boundary will SEGV on probe. This will cause false negatives - // for pointers that happen to be page aligned, which is undesirable but - // necessary for now. - // - // We minimize the consequences by using the pointer arithmetic flag in - // higher levels to conditionalize this suppression. - // - // TODO(tsepez): this may still fail for a non-accessible but non-null - // return from, say, malloc(0) which happens to be page-aligned. - // - // TODO(tsepez): enforce the pointer arithmetic flag. Until then, we - // may fail here if a pointer requires the flag but is lacking it. - return is_adjustable_ptr && - ((address & 0x0fff) == 0 || - __asan_region_is_poisoned(reinterpret_cast<void*>(address), 1)) && - !__asan_region_is_poisoned(reinterpret_cast<void*>(address - 1), 1); -} - -bool LikelySmuggledScalar(const volatile void* ptr) { - intptr_t address = reinterpret_cast<intptr_t>(ptr); - return address < 0x4000; // Negative or small positive. -} - -} // namespace base::internal
diff --git a/base/allocator/partition_allocator/pointers/raw_ptr_asan_unowned_impl.h b/base/allocator/partition_allocator/pointers/raw_ptr_asan_unowned_impl.h deleted file mode 100644 index dc9202e..0000000 --- a/base/allocator/partition_allocator/pointers/raw_ptr_asan_unowned_impl.h +++ /dev/null
@@ -1,153 +0,0 @@ -// 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. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_POINTERS_RAW_PTR_ASAN_UNOWNED_IMPL_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_POINTERS_RAW_PTR_ASAN_UNOWNED_IMPL_H_ - -#include <stddef.h> - -#include <type_traits> - -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/cxx20_is_constant_evaluated.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_forward.h" - -#if !BUILDFLAG(USE_ASAN_UNOWNED_PTR) -#error "Included under wrong build option" -#endif - -namespace base::internal { - -bool EndOfAliveAllocation(const volatile void* ptr, bool is_adjustable_ptr); -bool LikelySmuggledScalar(const volatile void* ptr); - -template <bool IsAdjustablePtr> -struct RawPtrAsanUnownedImpl { - // The first two are needed for correctness. The last one isn't technically a - // must, but better to set it. - static constexpr bool kMustZeroOnInit = true; - static constexpr bool kMustZeroOnMove = true; - static constexpr bool kMustZeroOnDestruct = true; - - // Wraps a pointer. - template <typename T> - PA_ALWAYS_INLINE static constexpr T* WrapRawPtr(T* ptr) { - return ptr; - } - - // Notifies the allocator when a wrapped pointer is being removed or replaced. - template <typename T> - PA_ALWAYS_INLINE static constexpr void ReleaseWrappedPtr(T* wrapped_ptr) { - if (!partition_alloc::internal::base::is_constant_evaluated()) { - ProbeForLowSeverityLifetimeIssue(wrapped_ptr); - } - } - - // Unwraps the pointer, while asserting that memory hasn't been freed. The - // function is allowed to crash on nullptr. - template <typename T> - PA_ALWAYS_INLINE static constexpr T* SafelyUnwrapPtrForDereference( - T* wrapped_ptr) { - // ASAN will catch use of dereferenced ptr without additional probing. - return wrapped_ptr; - } - - // Unwraps the pointer, while asserting that memory hasn't been freed. The - // function must handle nullptr gracefully. - template <typename T> - PA_ALWAYS_INLINE static constexpr T* SafelyUnwrapPtrForExtraction( - T* wrapped_ptr) { - if (!partition_alloc::internal::base::is_constant_evaluated()) { - ProbeForLowSeverityLifetimeIssue(wrapped_ptr); - } - return wrapped_ptr; - } - - // Unwraps the pointer, without making an assertion on whether memory was - // freed or not. - template <typename T> - PA_ALWAYS_INLINE static constexpr T* UnsafelyUnwrapPtrForComparison( - T* wrapped_ptr) { - return wrapped_ptr; - } - - // Upcasts the wrapped pointer. - template <typename To, typename From> - PA_ALWAYS_INLINE static constexpr To* Upcast(From* wrapped_ptr) { - static_assert(std::is_convertible<From*, To*>::value, - "From must be convertible to To."); - // Note, this cast may change the address if upcasting to base that lies in - // the middle of the derived object. - return wrapped_ptr; - } - - // Advance the wrapped pointer by `delta_elems`. - template < - typename T, - typename Z, - typename = - std::enable_if_t<partition_alloc::internal::is_offset_type<Z>, void>> - PA_ALWAYS_INLINE static constexpr T* Advance(T* wrapped_ptr, Z delta_elems) { - return wrapped_ptr + delta_elems; - } - - // Retreat the wrapped pointer by `delta_elems`. - template < - typename T, - typename Z, - typename = - std::enable_if_t<partition_alloc::internal::is_offset_type<Z>, void>> - PA_ALWAYS_INLINE static constexpr T* Retreat(T* wrapped_ptr, Z delta_elems) { - return wrapped_ptr - delta_elems; - } - - template <typename T> - PA_ALWAYS_INLINE static constexpr ptrdiff_t GetDeltaElems(T* wrapped_ptr1, - T* wrapped_ptr2) { - return wrapped_ptr1 - wrapped_ptr2; - } - - // Returns a copy of a wrapped pointer, without making an assertion on whether - // memory was freed or not. - template <typename T> - PA_ALWAYS_INLINE static constexpr T* Duplicate(T* wrapped_ptr) { - return wrapped_ptr; - } - - template <typename T> - static void ProbeForLowSeverityLifetimeIssue(T* wrapped_ptr) { - if (wrapped_ptr) { - const volatile void* probe_ptr = - reinterpret_cast<const volatile void*>(wrapped_ptr); - if (!LikelySmuggledScalar(probe_ptr) && - !EndOfAliveAllocation(probe_ptr, IsAdjustablePtr)) { - reinterpret_cast<const volatile uint8_t*>(probe_ptr)[0]; - } - } - } - - // `WrapRawPtrForDuplication` and `UnsafelyUnwrapPtrForDuplication` are used - // to create a new raw_ptr<T> from another raw_ptr<T> of a different flavor. - template <typename T> - PA_ALWAYS_INLINE static constexpr T* WrapRawPtrForDuplication(T* ptr) { - return ptr; - } - - template <typename T> - PA_ALWAYS_INLINE static constexpr T* UnsafelyUnwrapPtrForDuplication( - T* wrapped_ptr) { - return wrapped_ptr; - } - - // This is for accounting only, used by unit tests. - PA_ALWAYS_INLINE static constexpr void IncrementSwapCountForTest() {} - PA_ALWAYS_INLINE static constexpr void IncrementLessCountForTest() {} - PA_ALWAYS_INLINE static constexpr void - IncrementPointerToMemberOperatorCountForTest() {} -}; - -} // namespace base::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_POINTERS_RAW_PTR_ASAN_UNOWNED_IMPL_H_
diff --git a/base/allocator/partition_allocator/pointers/raw_ptr_backup_ref_impl.cc b/base/allocator/partition_allocator/pointers/raw_ptr_backup_ref_impl.cc deleted file mode 100644 index b0fda49..0000000 --- a/base/allocator/partition_allocator/pointers/raw_ptr_backup_ref_impl.cc +++ /dev/null
@@ -1,122 +0,0 @@ -// 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 "base/allocator/partition_allocator/pointers/raw_ptr_backup_ref_impl.h" - -#include <cstdint> - -#include "base/allocator/partition_allocator/dangling_raw_ptr_checks.h" -#include "base/allocator/partition_allocator/partition_alloc.h" -#include "base/allocator/partition_allocator/partition_alloc_base/check.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_ref_count.h" -#include "base/allocator/partition_allocator/partition_root.h" -#include "base/allocator/partition_allocator/reservation_offset_table.h" - -namespace base::internal { - -template <bool AllowDangling> -void RawPtrBackupRefImpl<AllowDangling>::AcquireInternal(uintptr_t address) { -#if BUILDFLAG(PA_DCHECK_IS_ON) || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) - PA_BASE_CHECK(partition_alloc::IsManagedByPartitionAllocBRPPool(address)); -#endif - uintptr_t slot_start = - partition_alloc::PartitionAllocGetSlotStartInBRPPool(address); - if constexpr (AllowDangling) { - partition_alloc::internal::PartitionRefCountPointer(slot_start) - ->AcquireFromUnprotectedPtr(); - } else { - partition_alloc::internal::PartitionRefCountPointer(slot_start)->Acquire(); - } -} - -template <bool AllowDangling> -void RawPtrBackupRefImpl<AllowDangling>::ReleaseInternal(uintptr_t address) { -#if BUILDFLAG(PA_DCHECK_IS_ON) || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) - PA_BASE_CHECK(partition_alloc::IsManagedByPartitionAllocBRPPool(address)); -#endif - uintptr_t slot_start = - partition_alloc::PartitionAllocGetSlotStartInBRPPool(address); - if constexpr (AllowDangling) { - if (partition_alloc::internal::PartitionRefCountPointer(slot_start) - ->ReleaseFromUnprotectedPtr()) { - partition_alloc::internal::PartitionAllocFreeForRefCounting(slot_start); - } - } else { - if (partition_alloc::internal::PartitionRefCountPointer(slot_start) - ->Release()) { - partition_alloc::internal::PartitionAllocFreeForRefCounting(slot_start); - } - } -} - -template <bool AllowDangling> -void RawPtrBackupRefImpl<AllowDangling>::ReportIfDanglingInternal( - uintptr_t address) { - if (partition_alloc::internal::IsUnretainedDanglingRawPtrCheckEnabled()) { - if (IsSupportedAndNotNull(address)) { - uintptr_t slot_start = - partition_alloc::PartitionAllocGetSlotStartInBRPPool(address); - partition_alloc::internal::PartitionRefCountPointer(slot_start) - ->ReportIfDangling(); - } - } -} - -// static -template <bool AllowDangling> -bool RawPtrBackupRefImpl<AllowDangling>::CheckPointerWithinSameAlloc( - uintptr_t before_addr, - uintptr_t after_addr, - size_t type_size) { - partition_alloc::internal::PtrPosWithinAlloc ptr_pos_within_alloc = - partition_alloc::internal::IsPtrWithinSameAlloc(before_addr, after_addr, - type_size); - // No need to check that |new_ptr| is in the same pool, as - // IsPtrWithinSameAlloc() checks that it's within the same allocation, so - // must be the same pool. - PA_BASE_CHECK(ptr_pos_within_alloc != - partition_alloc::internal::PtrPosWithinAlloc::kFarOOB); - -#if BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR) - return ptr_pos_within_alloc == - partition_alloc::internal::PtrPosWithinAlloc::kAllocEnd; -#else - return false; -#endif -} - -template <bool AllowDangling> -bool RawPtrBackupRefImpl<AllowDangling>::IsPointeeAlive(uintptr_t address) { -#if BUILDFLAG(PA_DCHECK_IS_ON) || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) - PA_BASE_CHECK(partition_alloc::IsManagedByPartitionAllocBRPPool(address)); -#endif - uintptr_t slot_start = - partition_alloc::PartitionAllocGetSlotStartInBRPPool(address); - return partition_alloc::internal::PartitionRefCountPointer(slot_start) - ->IsAlive(); -} - -// Explicitly instantiates the two BackupRefPtr variants in the .cc. This -// ensures the definitions not visible from the .h are available in the binary. -template struct RawPtrBackupRefImpl</*AllowDangling=*/false>; -template struct RawPtrBackupRefImpl</*AllowDangling=*/true>; - -#if BUILDFLAG(PA_DCHECK_IS_ON) || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) -void CheckThatAddressIsntWithinFirstPartitionPage(uintptr_t address) { - if (partition_alloc::internal::IsManagedByDirectMap(address)) { - uintptr_t reservation_start = - partition_alloc::internal::GetDirectMapReservationStart(address); - PA_BASE_CHECK(address - reservation_start >= - partition_alloc::PartitionPageSize()); - } else { - PA_BASE_CHECK(partition_alloc::internal::IsManagedByNormalBuckets(address)); - PA_BASE_CHECK(address % partition_alloc::kSuperPageSize >= - partition_alloc::PartitionPageSize()); - } -} -#endif // BUILDFLAG(PA_DCHECK_IS_ON) || - // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) - -} // namespace base::internal
diff --git a/base/allocator/partition_allocator/pointers/raw_ptr_backup_ref_impl.h b/base/allocator/partition_allocator/pointers/raw_ptr_backup_ref_impl.h deleted file mode 100644 index 69ee0d9..0000000 --- a/base/allocator/partition_allocator/pointers/raw_ptr_backup_ref_impl.h +++ /dev/null
@@ -1,468 +0,0 @@ -// 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. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_POINTERS_RAW_PTR_BACKUP_REF_IMPL_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_POINTERS_RAW_PTR_BACKUP_REF_IMPL_H_ - -#include <stddef.h> - -#include <type_traits> - -#include "base/allocator/partition_allocator/partition_address_space.h" -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_base/cxx20_is_constant_evaluated.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_config.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "base/allocator/partition_allocator/partition_alloc_forward.h" -#include "build/build_config.h" - -#if !BUILDFLAG(HAS_64_BIT_POINTERS) -#include "base/allocator/partition_allocator/address_pool_manager_bitmap.h" -#endif - -#if !BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) -#error "Included under wrong build option" -#endif - -namespace base::internal { - -#if BUILDFLAG(PA_DCHECK_IS_ON) || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) -PA_COMPONENT_EXPORT(RAW_PTR) -void CheckThatAddressIsntWithinFirstPartitionPage(uintptr_t address); -#endif - -// Note that `RawPtrBackupRefImpl` itself is not thread-safe. If multiple -// threads modify the same raw_ptr object without synchronization, a data race -// will occur. -template <bool AllowDangling = false> -struct RawPtrBackupRefImpl { - // These are needed for correctness, or else we may end up manipulating - // ref-count where we shouldn't, thus affecting the BRP's integrity. Unlike - // the first two, kMustZeroOnDestruct wouldn't be needed if raw_ptr was used - // correctly, but we already caught cases where a value is written after - // destruction. - static constexpr bool kMustZeroOnInit = true; - static constexpr bool kMustZeroOnMove = true; - static constexpr bool kMustZeroOnDestruct = true; - - private: - PA_ALWAYS_INLINE static bool IsSupportedAndNotNull(uintptr_t address) { - // There are many situations where the compiler can prove that - // `ReleaseWrappedPtr` is called on a value that is always nullptr, but the - // way `IsManagedByPartitionAllocBRPPool` is written, the compiler can't - // prove that nullptr is not managed by PartitionAlloc; and so the compiler - // has to emit a useless check and dead code. To avoid that without making - // the runtime check slower, tell the compiler to skip - // `IsManagedByPartitionAllocBRPPool` when it can statically determine that - // address is nullptr. -#if PA_HAS_BUILTIN(__builtin_constant_p) - if (__builtin_constant_p(address == 0) && (address == 0)) { -#if BUILDFLAG(PA_DCHECK_IS_ON) || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) - PA_BASE_CHECK( - !partition_alloc::IsManagedByPartitionAllocBRPPool(address)); -#endif // BUILDFLAG(PA_DCHECK_IS_ON) || - // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) - return false; - } -#endif // PA_HAS_BUILTIN(__builtin_constant_p) - - // This covers the nullptr case, as address 0 is never in any - // PartitionAlloc pool. - bool is_in_brp_pool = - partition_alloc::IsManagedByPartitionAllocBRPPool(address); - - // There may be pointers immediately after the allocation, e.g. - // { - // // Assume this allocation happens outside of PartitionAlloc. - // raw_ptr<T> ptr = new T[20]; - // for (size_t i = 0; i < 20; i ++) { ptr++; } - // } - // - // Such pointers are *not* at risk of accidentally falling into BRP pool, - // because: - // 1) On 64-bit systems, BRP pool is preceded by a forbidden region. - // 2) On 32-bit systems, the guard pages and metadata of super pages in BRP - // pool aren't considered to be part of that pool. - // - // This allows us to make a stronger assertion that if - // IsManagedByPartitionAllocBRPPool returns true for a valid pointer, - // it must be at least partition page away from the beginning of a super - // page. -#if BUILDFLAG(PA_DCHECK_IS_ON) || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) - if (is_in_brp_pool) { - CheckThatAddressIsntWithinFirstPartitionPage(address); - } -#endif - - return is_in_brp_pool; - } - -#if BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR) - // Out-Of-Bounds (OOB) poison bit is set when the pointer has overflowed by - // one byte. -#if defined(ARCH_CPU_X86_64) - // Bit 63 is the only pointer bit that will work as the poison bit across both - // LAM48 and LAM57. It also works when all unused linear address bits are - // checked for canonicality. - static constexpr uintptr_t OOB_POISON_BIT = static_cast<uintptr_t>(1) << 63; -#else - // Avoid ARM's Top-Byte Ignore. - static constexpr uintptr_t OOB_POISON_BIT = static_cast<uintptr_t>(1) << 55; -#endif - - template <typename T> - PA_ALWAYS_INLINE static T* UnpoisonPtr(T* ptr) { - return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(ptr) & - ~OOB_POISON_BIT); - } - - template <typename T> - PA_ALWAYS_INLINE static bool IsPtrOOB(T* ptr) { - return (reinterpret_cast<uintptr_t>(ptr) & OOB_POISON_BIT) == - OOB_POISON_BIT; - } - - template <typename T> - PA_ALWAYS_INLINE static T* PoisonOOBPtr(T* ptr) { - return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(ptr) | - OOB_POISON_BIT); - } -#else // BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR) - template <typename T> - PA_ALWAYS_INLINE static T* UnpoisonPtr(T* ptr) { - return ptr; - } -#endif // BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR) - - public: - // Wraps a pointer. - template <typename T> - PA_ALWAYS_INLINE static constexpr T* WrapRawPtr(T* ptr) { - if (partition_alloc::internal::base::is_constant_evaluated()) { - return ptr; - } - uintptr_t address = partition_alloc::UntagPtr(UnpoisonPtr(ptr)); - if (IsSupportedAndNotNull(address)) { -#if BUILDFLAG(PA_DCHECK_IS_ON) || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) - PA_BASE_CHECK(ptr != nullptr); -#endif - AcquireInternal(address); - } else { -#if !BUILDFLAG(HAS_64_BIT_POINTERS) -#if PA_HAS_BUILTIN(__builtin_constant_p) - // Similarly to `IsSupportedAndNotNull` above, elide the - // `BanSuperPageFromBRPPool` call if the compiler can prove that `address` - // is zero since PA won't be able to map anything at that address anyway. - bool known_constant_zero = - __builtin_constant_p(address == 0) && (address == 0); -#else // PA_HAS_BUILTIN(__builtin_constant_p) - bool known_constant_zero = false; -#endif // PA_HAS_BUILTIN(__builtin_constant_p) - - if (!known_constant_zero) { - partition_alloc::internal::AddressPoolManagerBitmap:: - BanSuperPageFromBRPPool(address); - } -#endif // !BUILDFLAG(HAS_64_BIT_POINTERS) - } - - return ptr; - } - - // Notifies the allocator when a wrapped pointer is being removed or replaced. - template <typename T> - PA_ALWAYS_INLINE static constexpr void ReleaseWrappedPtr(T* wrapped_ptr) { - if (partition_alloc::internal::base::is_constant_evaluated()) { - return; - } - uintptr_t address = partition_alloc::UntagPtr(UnpoisonPtr(wrapped_ptr)); - if (IsSupportedAndNotNull(address)) { -#if BUILDFLAG(PA_DCHECK_IS_ON) || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) - PA_BASE_CHECK(wrapped_ptr != nullptr); -#endif - ReleaseInternal(address); - } - // We are unable to counteract BanSuperPageFromBRPPool(), called from - // WrapRawPtr(). We only use one bit per super-page and, thus can't tell if - // there's more than one associated raw_ptr<T> at a given time. The risk of - // exhausting the entire address space is minuscule, therefore, we couldn't - // resist the perf gain of a single relaxed store (in the above mentioned - // function) over much more expensive two CAS operations, which we'd have to - // use if we were to un-ban a super-page. - } - - // Unwraps the pointer, while asserting that memory hasn't been freed. The - // function is allowed to crash on nullptr. - template <typename T> - PA_ALWAYS_INLINE static constexpr T* SafelyUnwrapPtrForDereference( - T* wrapped_ptr) { - if (partition_alloc::internal::base::is_constant_evaluated()) { - return wrapped_ptr; - } -#if BUILDFLAG(PA_DCHECK_IS_ON) || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) -#if BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR) - PA_BASE_CHECK(!IsPtrOOB(wrapped_ptr)); -#endif - uintptr_t address = partition_alloc::UntagPtr(wrapped_ptr); - if (IsSupportedAndNotNull(address)) { - PA_BASE_CHECK(wrapped_ptr != nullptr); - PA_BASE_CHECK(IsPointeeAlive(address)); - } -#endif // BUILDFLAG(PA_DCHECK_IS_ON) || - // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) - return wrapped_ptr; - } - - // Unwraps the pointer, while asserting that memory hasn't been freed. The - // function must handle nullptr gracefully. - template <typename T> - PA_ALWAYS_INLINE static constexpr T* SafelyUnwrapPtrForExtraction( - T* wrapped_ptr) { - if (partition_alloc::internal::base::is_constant_evaluated()) { - return wrapped_ptr; - } - T* unpoisoned_ptr = UnpoisonPtr(wrapped_ptr); -#if BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR) - // Some code uses invalid pointer values as indicators, so those values must - // be passed through unchanged during extraction. The following check will - // pass invalid values through if those values do not fall within the BRP - // pool after being unpoisoned. - if (!IsSupportedAndNotNull(partition_alloc::UntagPtr(unpoisoned_ptr))) { - return wrapped_ptr; - } - // Poison-based OOB checks do not extend to extracted pointers. The - // alternative of retaining poison on extracted pointers could introduce new - // OOB conditions, e.g., in code that extracts an end-of-allocation pointer - // for use in a loop termination condition. The poison bit would make that - // pointer appear to reference a very high address. -#endif // BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR) - return unpoisoned_ptr; - } - - // Unwraps the pointer, without making an assertion on whether memory was - // freed or not. - template <typename T> - PA_ALWAYS_INLINE static constexpr T* UnsafelyUnwrapPtrForComparison( - T* wrapped_ptr) { - if (partition_alloc::internal::base::is_constant_evaluated()) { - return wrapped_ptr; - } - // This may be used for unwrapping an end-of-allocation pointer to be used - // as an endpoint in an iterative algorithm, so this removes the OOB poison - // bit. - return UnpoisonPtr(wrapped_ptr); - } - - // Upcasts the wrapped pointer. - template <typename To, typename From> - PA_ALWAYS_INLINE static constexpr To* Upcast(From* wrapped_ptr) { - static_assert(std::is_convertible<From*, To*>::value, - "From must be convertible to To."); - // Note, this cast may change the address if upcasting to base that lies in - // the middle of the derived object. - return wrapped_ptr; - } - - // Verify the pointer stayed in the same slot, and return the poisoned version - // of `new_ptr` if OOB poisoning is enabled. - template <typename T> - PA_ALWAYS_INLINE static T* VerifyAndPoisonPointerAfterAdvanceOrRetreat( - T* unpoisoned_ptr, - T* new_ptr) { - // In the "before allocation" mode, on 32-bit, we can run into a problem - // that the end-of-allocation address could fall outside of - // PartitionAlloc's pools, if this is the last slot of the super page, - // thus pointing to the guard page. This means the ref-count won't be - // decreased when the pointer is released (leak). - // - // We could possibly solve it in a few different ways: - // - Add the trailing guard page to the pool, but we'd have to think very - // hard if this doesn't create another hole. - // - Add an address adjustment to "is in pool?" check, similar as the one in - // PartitionAllocGetSlotStartInBRPPool(), but that seems fragile, not to - // mention adding an extra instruction to an inlined hot path. - // - Let the leak happen, since it should a very rare condition. - // - Go back to the previous solution of rewrapping the pointer, but that - // had an issue of losing BRP protection in case the pointer ever gets - // shifted back before the end of allocation. - // - // We decided to cross that bridge once we get there... if we ever get - // there. Currently there are no plans to switch back to the "before - // allocation" mode. - // - // This problem doesn't exist in the "previous slot" mode, or any mode that - // involves putting extras after the allocation, because the - // end-of-allocation address belongs to the same slot. - static_assert(BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT)); - - // First check if the new address didn't migrate in/out the BRP pool, and - // that it lands within the same allocation. An end-of-allocation address is - // ok, too, and that may lead to the pointer being poisoned if the relevant - // feature is enabled. These checks add a non-trivial cost, but they're - // cheaper and more secure than the previous implementation that rewrapped - // the pointer (wrapped the new pointer and unwrapped the old one). - // - // Note, the value of these checks goes beyond OOB protection. They're - // important for integrity of the BRP algorithm. Without these, an attacker - // could make the pointer point to another allocation, and cause its - // ref-count to go to 0 upon this pointer's destruction, even though there - // may be another pointer still pointing to it, thus making it lose the BRP - // protection prematurely. - const uintptr_t before_addr = partition_alloc::UntagPtr(unpoisoned_ptr); - const uintptr_t after_addr = partition_alloc::UntagPtr(new_ptr); - // TODO(bartekn): Consider adding support for non-BRP pools too (without - // removing the cross-pool migration check). - if (IsSupportedAndNotNull(before_addr)) { - constexpr size_t size = sizeof(T); - [[maybe_unused]] const bool is_end = - CheckPointerWithinSameAlloc(before_addr, after_addr, size); -#if BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR) - if (is_end) { - new_ptr = PoisonOOBPtr(new_ptr); - } -#endif // BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR) - } else { - // Check that the new address didn't migrate into the BRP pool, as it - // would result in more pointers pointing to an allocation than its - // ref-count reflects. - PA_BASE_CHECK(!IsSupportedAndNotNull(after_addr)); - } - return new_ptr; - } - - // Advance the wrapped pointer by `delta_elems`. - template < - typename T, - typename Z, - typename = - std::enable_if_t<partition_alloc::internal::is_offset_type<Z>, void>> - PA_ALWAYS_INLINE static constexpr T* Advance(T* wrapped_ptr, Z delta_elems) { - if (partition_alloc::internal::base::is_constant_evaluated()) { - return wrapped_ptr + delta_elems; - } - T* unpoisoned_ptr = UnpoisonPtr(wrapped_ptr); - return VerifyAndPoisonPointerAfterAdvanceOrRetreat( - unpoisoned_ptr, unpoisoned_ptr + delta_elems); - } - - // Retreat the wrapped pointer by `delta_elems`. - template < - typename T, - typename Z, - typename = - std::enable_if_t<partition_alloc::internal::is_offset_type<Z>, void>> - PA_ALWAYS_INLINE static constexpr T* Retreat(T* wrapped_ptr, Z delta_elems) { - if (partition_alloc::internal::base::is_constant_evaluated()) { - return wrapped_ptr - delta_elems; - } - T* unpoisoned_ptr = UnpoisonPtr(wrapped_ptr); - return VerifyAndPoisonPointerAfterAdvanceOrRetreat( - unpoisoned_ptr, unpoisoned_ptr - delta_elems); - } - - template <typename T> - PA_ALWAYS_INLINE static constexpr ptrdiff_t GetDeltaElems(T* wrapped_ptr1, - T* wrapped_ptr2) { - if (partition_alloc::internal::base::is_constant_evaluated()) { - return wrapped_ptr1 - wrapped_ptr2; - } - - T* unpoisoned_ptr1 = UnpoisonPtr(wrapped_ptr1); - T* unpoisoned_ptr2 = UnpoisonPtr(wrapped_ptr2); -#if BUILDFLAG(ENABLE_POINTER_SUBTRACTION_CHECK) - if (partition_alloc::internal::base::is_constant_evaluated()) { - return unpoisoned_ptr1 - unpoisoned_ptr2; - } - uintptr_t address1 = partition_alloc::UntagPtr(unpoisoned_ptr1); - uintptr_t address2 = partition_alloc::UntagPtr(unpoisoned_ptr2); - // Ensure that both pointers are within the same slot, and pool! - // TODO(bartekn): Consider adding support for non-BRP pool too. - if (IsSupportedAndNotNull(address1)) { - PA_BASE_CHECK(IsSupportedAndNotNull(address2)); - PA_BASE_CHECK(partition_alloc::internal::IsPtrWithinSameAlloc( - address2, address1, sizeof(T)) != - partition_alloc::internal::PtrPosWithinAlloc::kFarOOB); - } else { - PA_BASE_CHECK(!IsSupportedAndNotNull(address2)); - } -#endif // BUILDFLAG(ENABLE_POINTER_SUBTRACTION_CHECK) - return unpoisoned_ptr1 - unpoisoned_ptr2; - } - - // Returns a copy of a wrapped pointer, without making an assertion on whether - // memory was freed or not. - // This method increments the reference count of the allocation slot. - template <typename T> - PA_ALWAYS_INLINE static constexpr T* Duplicate(T* wrapped_ptr) { - if (partition_alloc::internal::base::is_constant_evaluated()) { - return wrapped_ptr; - } - return WrapRawPtr(wrapped_ptr); - } - - // Report the current wrapped pointer if pointee isn't alive anymore. - template <typename T> - PA_ALWAYS_INLINE static void ReportIfDangling(T* wrapped_ptr) { - ReportIfDanglingInternal(partition_alloc::UntagPtr(wrapped_ptr)); - } - - // `WrapRawPtrForDuplication` and `UnsafelyUnwrapPtrForDuplication` are used - // to create a new raw_ptr<T> from another raw_ptr<T> of a different flavor. - template <typename T> - PA_ALWAYS_INLINE static constexpr T* WrapRawPtrForDuplication(T* ptr) { - if (partition_alloc::internal::base::is_constant_evaluated()) { - return ptr; - } else { - return WrapRawPtr(ptr); - } - } - - template <typename T> - PA_ALWAYS_INLINE static constexpr T* UnsafelyUnwrapPtrForDuplication( - T* wrapped_ptr) { - if (partition_alloc::internal::base::is_constant_evaluated()) { - return wrapped_ptr; - } else { - return UnpoisonPtr(wrapped_ptr); - } - } - - // This is for accounting only, used by unit tests. - PA_ALWAYS_INLINE static constexpr void IncrementSwapCountForTest() {} - PA_ALWAYS_INLINE static constexpr void IncrementLessCountForTest() {} - PA_ALWAYS_INLINE static constexpr void - IncrementPointerToMemberOperatorCountForTest() {} - - private: - // We've evaluated several strategies (inline nothing, various parts, or - // everything in |Wrap()| and |Release()|) using the Speedometer2 benchmark - // to measure performance. The best results were obtained when only the - // lightweight |IsManagedByPartitionAllocBRPPool()| check was inlined. - // Therefore, we've extracted the rest into the functions below and marked - // them as PA_NOINLINE to prevent unintended LTO effects. - PA_NOINLINE static PA_COMPONENT_EXPORT(RAW_PTR) void AcquireInternal( - uintptr_t address); - PA_NOINLINE static PA_COMPONENT_EXPORT(RAW_PTR) void ReleaseInternal( - uintptr_t address); - PA_NOINLINE static PA_COMPONENT_EXPORT(RAW_PTR) bool IsPointeeAlive( - uintptr_t address); - PA_NOINLINE static PA_COMPONENT_EXPORT(RAW_PTR) void ReportIfDanglingInternal( - uintptr_t address); - - // CHECK if `before_addr` and `after_addr` are in the same allocation, for a - // given `type_size`. - // If BACKUP_REF_PTR_POISON_OOB_PTR is enabled, return whether the allocation - // is at the end. - // If BACKUP_REF_PTR_POISON_OOB_PTR is disable, return false. - PA_NOINLINE static PA_COMPONENT_EXPORT( - RAW_PTR) bool CheckPointerWithinSameAlloc(uintptr_t before_addr, - uintptr_t after_addr, - size_t type_size); -}; - -} // namespace base::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_POINTERS_RAW_PTR_BACKUP_REF_IMPL_H_
diff --git a/base/allocator/partition_allocator/pointers/raw_ptr_exclusion.h b/base/allocator/partition_allocator/pointers/raw_ptr_exclusion.h deleted file mode 100644 index dc94d6b..0000000 --- a/base/allocator/partition_allocator/pointers/raw_ptr_exclusion.h +++ /dev/null
@@ -1,37 +0,0 @@ -// 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. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_POINTERS_RAW_PTR_EXCLUSION_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_POINTERS_RAW_PTR_EXCLUSION_H_ - -// This header will be leakily included even when -// `!use_partition_alloc`, which is okay because it's a leaf header. -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" // nogncheck -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "build/build_config.h" - -#if PA_HAS_ATTRIBUTE(annotate) -#if defined(OFFICIAL_BUILD) && !BUILDFLAG(FORCE_ENABLE_RAW_PTR_EXCLUSION) -// The annotation changed compiler output and increased binary size so disable -// for official builds. -// TODO(crbug.com/1320670): Remove when issue is resolved. -#define RAW_PTR_EXCLUSION -#else -// Marks a field as excluded from the `raw_ptr<T>` usage enforcement via -// Chromium Clang plugin. -// -// Example: -// RAW_PTR_EXCLUSION Foo* foo_; -// -// `RAW_PTR_EXCLUSION` should be avoided, as exclusions makes it significantly -// easier for any bug involving the pointer to become a security vulnerability. -// For additional guidance please see the "When to use raw_ptr<T>" section of -// `//base/memory/raw_ptr.md`. -#define RAW_PTR_EXCLUSION __attribute__((annotate("raw_ptr_exclusion"))) -#endif -#else -#define RAW_PTR_EXCLUSION -#endif - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_POINTERS_RAW_PTR_EXCLUSION_H_
diff --git a/base/allocator/partition_allocator/pointers/raw_ptr_hookable_impl.cc b/base/allocator/partition_allocator/pointers/raw_ptr_hookable_impl.cc deleted file mode 100644 index 1e8ca8a..0000000 --- a/base/allocator/partition_allocator/pointers/raw_ptr_hookable_impl.cc +++ /dev/null
@@ -1,48 +0,0 @@ -// 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 "base/allocator/partition_allocator/pointers/raw_ptr_hookable_impl.h" - -#include <atomic> -#include <cstdint> - -namespace base::internal { - -namespace { - -void DefaultWrapPtrHook(uintptr_t address) {} -void DefaultReleaseWrappedPtrHook(uintptr_t address) {} -void DefaultUnwrapForDereferenceHook(uintptr_t address) {} -void DefaultUnwrapForExtractionHook(uintptr_t address) {} -void DefaultUnwrapForComparisonHook(uintptr_t address) {} -void DefaultAdvanceHook(uintptr_t old_address, uintptr_t new_address) {} -void DefaultDuplicateHook(uintptr_t address) {} - -constexpr RawPtrHooks default_hooks = { - DefaultWrapPtrHook, - DefaultReleaseWrappedPtrHook, - DefaultUnwrapForDereferenceHook, - DefaultUnwrapForExtractionHook, - DefaultUnwrapForComparisonHook, - DefaultAdvanceHook, - DefaultDuplicateHook, -}; - -} // namespace - -std::atomic<const RawPtrHooks*> g_hooks{&default_hooks}; - -const RawPtrHooks* GetRawPtrHooks() { - return g_hooks.load(std::memory_order_relaxed); -} - -void InstallRawPtrHooks(const RawPtrHooks* hooks) { - g_hooks.store(hooks, std::memory_order_relaxed); -} - -void ResetRawPtrHooks() { - InstallRawPtrHooks(&default_hooks); -} - -} // namespace base::internal
diff --git a/base/allocator/partition_allocator/pointers/raw_ptr_hookable_impl.h b/base/allocator/partition_allocator/pointers/raw_ptr_hookable_impl.h deleted file mode 100644 index a47d0dd..0000000 --- a/base/allocator/partition_allocator/pointers/raw_ptr_hookable_impl.h +++ /dev/null
@@ -1,183 +0,0 @@ -// 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. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_POINTERS_RAW_PTR_HOOKABLE_IMPL_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_POINTERS_RAW_PTR_HOOKABLE_IMPL_H_ - -#include <stddef.h> - -#include <type_traits> - -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_base/cxx20_is_constant_evaluated.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_forward.h" - -#if !BUILDFLAG(USE_HOOKABLE_RAW_PTR) -#error "Included under wrong build option" -#endif - -namespace base::internal { - -struct RawPtrHooks { - using WrapPtr = void(uintptr_t address); - using ReleaseWrappedPtr = void(uintptr_t address); - using SafelyUnwrapForDereference = void(uintptr_t address); - using SafelyUnwrapForExtraction = void(uintptr_t address); - using UnsafelyUnwrapForComparison = void(uintptr_t address); - using Advance = void(uintptr_t old_address, uintptr_t new_address); - using Duplicate = void(uintptr_t address); - - WrapPtr* wrap_ptr; - ReleaseWrappedPtr* release_wrapped_ptr; - SafelyUnwrapForDereference* safely_unwrap_for_dereference; - SafelyUnwrapForExtraction* safely_unwrap_for_extraction; - UnsafelyUnwrapForComparison* unsafely_unwrap_for_comparison; - Advance* advance; - Duplicate* duplicate; -}; - -PA_COMPONENT_EXPORT(RAW_PTR) const RawPtrHooks* GetRawPtrHooks(); -PA_COMPONENT_EXPORT(RAW_PTR) void InstallRawPtrHooks(const RawPtrHooks*); -PA_COMPONENT_EXPORT(RAW_PTR) void ResetRawPtrHooks(); - -struct RawPtrHookableImpl { - // Since this Impl is used for BRP-ASan, match BRP as closely as possible. - static constexpr bool kMustZeroOnInit = true; - static constexpr bool kMustZeroOnMove = true; - static constexpr bool kMustZeroOnDestruct = true; - - // Wraps a pointer. - template <typename T> - PA_ALWAYS_INLINE static constexpr T* WrapRawPtr(T* ptr) { - if (!partition_alloc::internal::base::is_constant_evaluated()) { - GetRawPtrHooks()->wrap_ptr(reinterpret_cast<uintptr_t>(ptr)); - } - return ptr; - } - - // Notifies the allocator when a wrapped pointer is being removed or replaced. - template <typename T> - PA_ALWAYS_INLINE static constexpr void ReleaseWrappedPtr(T* ptr) { - if (!partition_alloc::internal::base::is_constant_evaluated()) { - GetRawPtrHooks()->release_wrapped_ptr(reinterpret_cast<uintptr_t>(ptr)); - } - } - - // Unwraps the pointer, while asserting that memory hasn't been freed. The - // function is allowed to crash on nullptr. - template <typename T> - PA_ALWAYS_INLINE static constexpr T* SafelyUnwrapPtrForDereference( - T* wrapped_ptr) { - if (!partition_alloc::internal::base::is_constant_evaluated()) { - GetRawPtrHooks()->safely_unwrap_for_dereference( - reinterpret_cast<uintptr_t>(wrapped_ptr)); - } - return wrapped_ptr; - } - - // Unwraps the pointer, while asserting that memory hasn't been freed. The - // function must handle nullptr gracefully. - template <typename T> - PA_ALWAYS_INLINE static constexpr T* SafelyUnwrapPtrForExtraction( - T* wrapped_ptr) { - if (!partition_alloc::internal::base::is_constant_evaluated()) { - GetRawPtrHooks()->safely_unwrap_for_extraction( - reinterpret_cast<uintptr_t>(wrapped_ptr)); - } - return wrapped_ptr; - } - - // Unwraps the pointer, without making an assertion on whether memory was - // freed or not. - template <typename T> - PA_ALWAYS_INLINE static constexpr T* UnsafelyUnwrapPtrForComparison( - T* wrapped_ptr) { - if (!partition_alloc::internal::base::is_constant_evaluated()) { - GetRawPtrHooks()->unsafely_unwrap_for_comparison( - reinterpret_cast<uintptr_t>(wrapped_ptr)); - } - return wrapped_ptr; - } - - // Upcasts the wrapped pointer. - template <typename To, typename From> - PA_ALWAYS_INLINE static constexpr To* Upcast(From* wrapped_ptr) { - static_assert(std::is_convertible<From*, To*>::value, - "From must be convertible to To."); - // Note, this cast may change the address if upcasting to base that lies in - // the middle of the derived object. - return wrapped_ptr; - } - - // Advance the wrapped pointer by `delta_elems`. - template < - typename T, - typename Z, - typename = - std::enable_if_t<partition_alloc::internal::is_offset_type<Z>, void>> - PA_ALWAYS_INLINE static constexpr T* Advance(T* wrapped_ptr, Z delta_elems) { - if (!partition_alloc::internal::base::is_constant_evaluated()) { - GetRawPtrHooks()->advance( - reinterpret_cast<uintptr_t>(wrapped_ptr), - reinterpret_cast<uintptr_t>(wrapped_ptr + delta_elems)); - } - return wrapped_ptr + delta_elems; - } - - // Retreat the wrapped pointer by `delta_elems`. - template < - typename T, - typename Z, - typename = - std::enable_if_t<partition_alloc::internal::is_offset_type<Z>, void>> - PA_ALWAYS_INLINE static constexpr T* Retreat(T* wrapped_ptr, Z delta_elems) { - if (!partition_alloc::internal::base::is_constant_evaluated()) { - GetRawPtrHooks()->advance( - reinterpret_cast<uintptr_t>(wrapped_ptr), - reinterpret_cast<uintptr_t>(wrapped_ptr - delta_elems)); - } - return wrapped_ptr - delta_elems; - } - - template <typename T> - PA_ALWAYS_INLINE static constexpr ptrdiff_t GetDeltaElems(T* wrapped_ptr1, - T* wrapped_ptr2) { - return wrapped_ptr1 - wrapped_ptr2; - } - - // Returns a copy of a wrapped pointer, without making an assertion on whether - // memory was freed or not. - template <typename T> - PA_ALWAYS_INLINE static constexpr T* Duplicate(T* wrapped_ptr) { - if (!partition_alloc::internal::base::is_constant_evaluated()) { - GetRawPtrHooks()->duplicate(reinterpret_cast<uintptr_t>(wrapped_ptr)); - } - return wrapped_ptr; - } - - // `WrapRawPtrForDuplication` and `UnsafelyUnwrapPtrForDuplication` are used - // to create a new raw_ptr<T> from another raw_ptr<T> of a different flavor. - template <typename T> - PA_ALWAYS_INLINE static constexpr T* WrapRawPtrForDuplication(T* ptr) { - return ptr; - } - - template <typename T> - PA_ALWAYS_INLINE static constexpr T* UnsafelyUnwrapPtrForDuplication( - T* wrapped_ptr) { - return wrapped_ptr; - } - - // This is for accounting only, used by unit tests. - PA_ALWAYS_INLINE static constexpr void IncrementSwapCountForTest() {} - PA_ALWAYS_INLINE static constexpr void IncrementLessCountForTest() {} - PA_ALWAYS_INLINE static constexpr void - IncrementPointerToMemberOperatorCountForTest() {} -}; - -} // namespace base::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_POINTERS_RAW_PTR_HOOKABLE_IMPL_H_
diff --git a/base/allocator/partition_allocator/pointers/raw_ptr_test_support.h b/base/allocator/partition_allocator/pointers/raw_ptr_test_support.h deleted file mode 100644 index df84b10..0000000 --- a/base/allocator/partition_allocator/pointers/raw_ptr_test_support.h +++ /dev/null
@@ -1,76 +0,0 @@ -// 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. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_POINTERS_RAW_PTR_TEST_SUPPORT_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_POINTERS_RAW_PTR_TEST_SUPPORT_H_ - -#include "testing/gmock/include/gmock/gmock.h" -#include "third_party/abseil-cpp/absl/types/optional.h" - -// Struct intended to be used with designated initializers and passed -// to the `CountersMatch()` matcher. -// -// `CountingImplType` isn't used directly; it tells the `CountersMatch` -// matcher which impl's static members should be checked. -template <typename CountingImplType> -struct CountingRawPtrExpectations { - absl::optional<int> wrap_raw_ptr_cnt; - absl::optional<int> release_wrapped_ptr_cnt; - absl::optional<int> get_for_dereference_cnt; - absl::optional<int> get_for_extraction_cnt; - absl::optional<int> get_for_comparison_cnt; - absl::optional<int> wrapped_ptr_swap_cnt; - absl::optional<int> wrapped_ptr_less_cnt; - absl::optional<int> pointer_to_member_operator_cnt; - absl::optional<int> wrap_raw_ptr_for_dup_cnt; - absl::optional<int> get_for_duplication_cnt; -}; - -#define REPORT_UNEQUAL_RAW_PTR_COUNTER(member_name, CounterClassImpl) \ - { \ - if (arg.member_name.has_value() && \ - arg.member_name.value() != CounterClassImpl::member_name) { \ - *result_listener << "Expected `" #member_name "` to be " \ - << arg.member_name.value() << " but got " \ - << CounterClassImpl::member_name << "; "; \ - result = false; \ - } \ - } -#define REPORT_UNEQUAL_RAW_PTR_COUNTERS(result, CounterClassImpl) \ - { \ - result = true; \ - REPORT_UNEQUAL_RAW_PTR_COUNTER(wrap_raw_ptr_cnt, CounterClassImpl) \ - REPORT_UNEQUAL_RAW_PTR_COUNTER(release_wrapped_ptr_cnt, CounterClassImpl) \ - REPORT_UNEQUAL_RAW_PTR_COUNTER(get_for_dereference_cnt, CounterClassImpl) \ - REPORT_UNEQUAL_RAW_PTR_COUNTER(get_for_extraction_cnt, CounterClassImpl) \ - REPORT_UNEQUAL_RAW_PTR_COUNTER(get_for_comparison_cnt, CounterClassImpl) \ - REPORT_UNEQUAL_RAW_PTR_COUNTER(wrapped_ptr_swap_cnt, CounterClassImpl) \ - REPORT_UNEQUAL_RAW_PTR_COUNTER(wrapped_ptr_less_cnt, CounterClassImpl) \ - REPORT_UNEQUAL_RAW_PTR_COUNTER(pointer_to_member_operator_cnt, \ - CounterClassImpl) \ - REPORT_UNEQUAL_RAW_PTR_COUNTER(wrap_raw_ptr_for_dup_cnt, CounterClassImpl) \ - REPORT_UNEQUAL_RAW_PTR_COUNTER(get_for_duplication_cnt, CounterClassImpl) \ - } - -// Matcher used with `CountingRawPtr`. Provides slightly shorter -// boilerplate for verifying counts. This inner function is detached -// from the `MATCHER` to isolate the templating. -template <typename CountingImplType> -bool CountersMatchImpl(const CountingRawPtrExpectations<CountingImplType>& arg, - testing::MatchResultListener* result_listener) { - bool result = true; - REPORT_UNEQUAL_RAW_PTR_COUNTERS(result, CountingImplType); - return result; -} - -// Implicit `arg` has type `CountingRawPtrExpectations`, specialized for -// the specific counting impl. -MATCHER(CountersMatch, "counting impl has specified counters") { - return CountersMatchImpl(arg, result_listener); -} - -#undef REPORT_UNEQUAL_RAW_PTR_COUNTERS -#undef REPORT_UNEQUAL_RAW_PTR_COUNTER - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_POINTERS_RAW_PTR_TEST_SUPPORT_H_
diff --git a/base/allocator/partition_allocator/pointers/raw_ptr_unittest.cc b/base/allocator/partition_allocator/pointers/raw_ptr_unittest.cc deleted file mode 100644 index 7644478..0000000 --- a/base/allocator/partition_allocator/pointers/raw_ptr_unittest.cc +++ /dev/null
@@ -1,2428 +0,0 @@ -// 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. - -#include "base/allocator/partition_allocator/pointers/raw_ptr.h" - -#include <climits> -#include <cstddef> -#include <cstdint> -#include <string> -#include <thread> -#include <type_traits> -#include <utility> - -#include "base/allocator/partition_alloc_features.h" -#include "base/allocator/partition_alloc_support.h" -#include "base/allocator/partition_allocator/dangling_raw_ptr_checks.h" -#include "base/allocator/partition_allocator/partition_alloc-inl.h" -#include "base/allocator/partition_allocator/partition_alloc.h" -#include "base/allocator/partition_allocator/partition_alloc_base/numerics/checked_math.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_config.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "base/allocator/partition_allocator/partition_alloc_hooks.h" -#include "base/allocator/partition_allocator/pointers/raw_ptr_test_support.h" -#include "base/allocator/partition_allocator/pointers/raw_ref.h" -#include "base/allocator/partition_allocator/tagging.h" -#include "base/cpu.h" -#include "base/cxx20_to_address.h" -#include "base/logging.h" -#include "base/memory/raw_ptr_asan_service.h" -#include "base/task/thread_pool.h" -#include "base/test/bind.h" -#include "base/test/gtest_util.h" -#include "base/test/memory/dangling_ptr_instrumentation.h" -#include "base/test/scoped_feature_list.h" -#include "base/test/task_environment.h" -#include "build/build_config.h" -#include "build/buildflag.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/abseil-cpp/absl/types/optional.h" -#include "third_party/abseil-cpp/absl/types/variant.h" - -#if BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) -#include <sanitizer/asan_interface.h> -#include "base/debug/asan_service.h" -#endif - -using testing::AllOf; -using testing::HasSubstr; -using testing::Test; - -static_assert(sizeof(raw_ptr<void>) == sizeof(void*), - "raw_ptr shouldn't add memory overhead"); -static_assert(sizeof(raw_ptr<int>) == sizeof(int*), - "raw_ptr shouldn't add memory overhead"); -static_assert(sizeof(raw_ptr<std::string>) == sizeof(std::string*), - "raw_ptr shouldn't add memory overhead"); - -#if !BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) && \ - !BUILDFLAG(USE_ASAN_UNOWNED_PTR) && !BUILDFLAG(USE_HOOKABLE_RAW_PTR) -// |is_trivially_copyable| assertion means that arrays/vectors of raw_ptr can -// be copied by memcpy. -static_assert(std::is_trivially_copyable<raw_ptr<void>>::value, - "raw_ptr should be trivially copyable"); -static_assert(std::is_trivially_copyable<raw_ptr<int>>::value, - "raw_ptr should be trivially copyable"); -static_assert(std::is_trivially_copyable<raw_ptr<std::string>>::value, - "raw_ptr should be trivially copyable"); - -// |is_trivially_default_constructible| assertion helps retain implicit default -// constructors when raw_ptr is used as a union field. Example of an error -// if this assertion didn't hold: -// -// ../../base/trace_event/trace_arguments.h:249:16: error: call to -// implicitly-deleted default constructor of 'base::trace_event::TraceValue' -// TraceValue ret; -// ^ -// ../../base/trace_event/trace_arguments.h:211:26: note: default -// constructor of 'TraceValue' is implicitly deleted because variant field -// 'as_pointer' has a non-trivial default constructor -// raw_ptr<const void> as_pointer; -static_assert(std::is_trivially_default_constructible<raw_ptr<void>>::value, - "raw_ptr should be trivially default constructible"); -static_assert(std::is_trivially_default_constructible<raw_ptr<int>>::value, - "raw_ptr should be trivially default constructible"); -static_assert( - std::is_trivially_default_constructible<raw_ptr<std::string>>::value, - "raw_ptr should be trivially default constructible"); -#endif // !BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) && - // !BUILDFLAG(USE_ASAN_UNOWNED_PTR) && !BUILDFLAG(USE_HOOKABLE_RAW_PTR) - -// Verify that raw_ptr is a literal type, and its entire interface is constexpr. -// -// Constexpr destructors were introduced in C++20. PartitionAlloc's minimum -// supported C++ version is C++17, so raw_ptr is not a literal type in C++17. -// Thus we only test for constexpr in C++20. -#if defined(__cpp_constexpr) && __cpp_constexpr >= 201907L -static_assert([]() constexpr { - struct IntBase {}; - struct Int : public IntBase { - int i = 0; - }; - - Int* i = new Int(); - { - raw_ptr<Int> r(i); // raw_ptr(T*) - raw_ptr<Int> r2(r); // raw_ptr(const raw_ptr&) - raw_ptr<Int> r3(std::move(r)); // raw_ptr(raw_ptr&&) - r = r2; // operator=(const raw_ptr&) - r = std::move(r3); // operator=(raw_ptr&&) - raw_ptr<Int, base::RawPtrTraits::kMayDangle> r4( - r); // raw_ptr(const raw_ptr<DifferentTraits>&) - r4 = r2; // operator=(const raw_ptr<DifferentTraits>&) - // (There is no move-version of DifferentTraits.) - [[maybe_unused]] raw_ptr<IntBase> r5( - r2); // raw_ptr(const raw_ptr<Convertible>&) - [[maybe_unused]] raw_ptr<IntBase> r6( - std::move(r2)); // raw_ptr(raw_ptr<Convertible>&&) - r2 = r; // Reset after move... - r5 = r2; // operator=(const raw_ptr<Convertible>&) - r5 = std::move(r2); // operator=(raw_ptr<Convertible>&&) - [[maybe_unused]] raw_ptr<Int> r7(nullptr); // raw_ptr(nullptr) - r4 = nullptr; // operator=(nullptr) - r4 = i; // operator=(T*) - r5 = r4; // operator=(const Upcast&) - r5 = std::move(r4); // operator=(Upcast&&) - r.get()->i += 1; // get() - [[maybe_unused]] bool b = r; // operator bool - (*r).i += 1; // operator*() - r->i += 1; // operator->() - [[maybe_unused]] Int* i2 = r; // operator T*() - [[maybe_unused]] IntBase* i3 = r; // operator Convertible*() - - Int* array = new Int[3](); - { - raw_ptr<Int, base::RawPtrTraits::kAllowPtrArithmetic> ra(array); - ++ra; // operator++() - --ra; // operator--() - ra++; // operator++(int) - ra--; // operator--(int) - ra += 1u; // operator+=() - ra -= 1u; // operator-=() - } - delete[] array; - } - delete i; - return true; -}()); -#endif - -// Don't use base::internal for testing raw_ptr API, to test if code outside -// this namespace calls the correct functions from this namespace. -namespace { - -// `kAllowPtrArithmetic` matches what `CountingRawPtr` does internally. -// `kUseCountingWrapperForTest` is removed. -using RawPtrCountingImpl = base::internal::RawPtrCountingImplWrapperForTest< - base::RawPtrTraits::kAllowPtrArithmetic>; - -// `kMayDangle | kAllowPtrArithmetic` matches what `CountingRawPtrMayDangle` -// does internally. `kUseCountingWrapperForTest` is removed, and `kMayDangle` -// and `kAllowPtrArithmetic` are kept. -using RawPtrCountingMayDangleImpl = - base::internal::RawPtrCountingImplWrapperForTest< - base::RawPtrTraits::kMayDangle | - base::RawPtrTraits::kAllowPtrArithmetic>; - -template <typename T> -using CountingRawPtr = raw_ptr<T, - base::RawPtrTraits::kUseCountingWrapperForTest | - base::RawPtrTraits::kAllowPtrArithmetic>; -static_assert(std::is_same_v<CountingRawPtr<int>::Impl, RawPtrCountingImpl>); - -template <typename T> -using CountingRawPtrMayDangle = - raw_ptr<T, - base::RawPtrTraits::kMayDangle | - base::RawPtrTraits::kUseCountingWrapperForTest | - base::RawPtrTraits::kAllowPtrArithmetic>; -static_assert(std::is_same_v<CountingRawPtrMayDangle<int>::Impl, - RawPtrCountingMayDangleImpl>); - -struct MyStruct { - int x; -}; - -struct Base1 { - explicit Base1(int b1) : b1(b1) {} - int b1; -}; - -struct Base2 { - explicit Base2(int b2) : b2(b2) {} - int b2; -}; - -struct Derived : Base1, Base2 { - Derived(int b1, int b2, int d) : Base1(b1), Base2(b2), d(d) {} - int d; -}; - -class RawPtrTest : public Test { - protected: - void SetUp() override { - RawPtrCountingImpl::ClearCounters(); - RawPtrCountingMayDangleImpl::ClearCounters(); - } -}; - -// Use this instead of std::ignore, to prevent the instruction from getting -// optimized out by the compiler. -volatile int g_volatile_int_to_ignore; - -TEST_F(RawPtrTest, NullStarDereference) { - raw_ptr<int> ptr = nullptr; - EXPECT_DEATH_IF_SUPPORTED(g_volatile_int_to_ignore = *ptr, ""); -} - -TEST_F(RawPtrTest, NullArrowDereference) { - raw_ptr<MyStruct> ptr = nullptr; - EXPECT_DEATH_IF_SUPPORTED(g_volatile_int_to_ignore = ptr->x, ""); -} - -TEST_F(RawPtrTest, NullExtractNoDereference) { - CountingRawPtr<int> ptr = nullptr; - // No dereference hence shouldn't crash. - int* raw = ptr; - std::ignore = raw; - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 1, - .get_for_comparison_cnt = 0}), - CountersMatch()); -} - -TEST_F(RawPtrTest, InvalidExtractNoDereference) { - // Some code uses invalid pointer values as indicators, so those values must - // be accepted by raw_ptr and passed through unchanged during extraction. - int* inv_ptr = reinterpret_cast<int*>(~static_cast<uintptr_t>(0)); - CountingRawPtr<int> ptr = inv_ptr; - int* raw = ptr; - EXPECT_EQ(raw, inv_ptr); - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 1, - .get_for_comparison_cnt = 0}), - CountersMatch()); -} - -TEST_F(RawPtrTest, NullCmpExplicit) { - CountingRawPtr<int> ptr = nullptr; - EXPECT_TRUE(ptr == nullptr); - EXPECT_TRUE(nullptr == ptr); - EXPECT_FALSE(ptr != nullptr); - EXPECT_FALSE(nullptr != ptr); - // No need to unwrap pointer, just compare against 0. - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = 0, - }), - CountersMatch()); -} - -TEST_F(RawPtrTest, NullCmpBool) { - CountingRawPtr<int> ptr = nullptr; - EXPECT_FALSE(ptr); - EXPECT_TRUE(!ptr); - // No need to unwrap pointer, just compare against 0. - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = 0, - }), - CountersMatch()); -} - -void FuncThatAcceptsBool(bool b) {} - -bool IsValidNoCast(CountingRawPtr<int> ptr) { - return !!ptr; // !! to avoid implicit cast -} -bool IsValidNoCast2(CountingRawPtr<int> ptr) { - return ptr && true; -} - -TEST_F(RawPtrTest, BoolOpNotCast) { - CountingRawPtr<int> ptr = nullptr; - volatile bool is_valid = !!ptr; // !! to avoid implicit cast - is_valid = ptr || is_valid; // volatile, so won't be optimized - if (ptr) { - is_valid = true; - } - [[maybe_unused]] bool is_not_valid = !ptr; - if (!ptr) { - is_not_valid = true; - } - std::ignore = IsValidNoCast(ptr); - std::ignore = IsValidNoCast2(ptr); - FuncThatAcceptsBool(!ptr); - // No need to unwrap pointer, just compare against 0. - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = 0, - }), - CountersMatch()); -} - -bool IsValidWithCast(CountingRawPtr<int> ptr) { - return ptr; -} - -// This test is mostly for documentation purposes. It demonstrates cases where -// |operator T*| is called first and then the pointer is converted to bool, -// as opposed to calling |operator bool| directly. The former may be more -// costly, so the caller has to be careful not to trigger this path. -TEST_F(RawPtrTest, CastNotBoolOp) { - CountingRawPtr<int> ptr = nullptr; - [[maybe_unused]] bool is_valid = ptr; - is_valid = IsValidWithCast(ptr); - FuncThatAcceptsBool(ptr); - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 3, - .get_for_comparison_cnt = 0, - }), - CountersMatch()); -} - -TEST_F(RawPtrTest, StarDereference) { - int foo = 42; - CountingRawPtr<int> ptr = &foo; - EXPECT_EQ(*ptr, 42); - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 1, - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = 0, - }), - CountersMatch()); -} - -TEST_F(RawPtrTest, ArrowDereference) { - MyStruct foo = {42}; - CountingRawPtr<MyStruct> ptr = &foo; - EXPECT_EQ(ptr->x, 42); - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 1, - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = 0, - }), - CountersMatch()); -} - -TEST_F(RawPtrTest, Delete) { - CountingRawPtr<int> ptr = new int(42); - delete ptr.ExtractAsDangling(); - // The pointer is first internally converted to MayDangle kind, then extracted - // using implicit cast before passing to |delete|. - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = 0, - .wrap_raw_ptr_for_dup_cnt = 0, - .get_for_duplication_cnt = 1, - }), - CountersMatch()); - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingMayDangleImpl>{ - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 1, - .get_for_comparison_cnt = 0, - .wrap_raw_ptr_for_dup_cnt = 1, - .get_for_duplication_cnt = 0, - }), - CountersMatch()); -} - -TEST_F(RawPtrTest, ClearAndDelete) { - CountingRawPtr<int> ptr(new int); - ptr.ClearAndDelete(); - - // TODO(crbug.com/1346513): clang-format has a difficult time making - // sense of preprocessor arms mixed with designated initializers. - // - // clang-format off - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl> { - .wrap_raw_ptr_cnt = 1, - .release_wrapped_ptr_cnt = 1, - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 1, - .wrapped_ptr_swap_cnt = 0, - }), - CountersMatch()); - // clang-format on - EXPECT_EQ(ptr.get(), nullptr); -} - -TEST_F(RawPtrTest, ClearAndDeleteArray) { - CountingRawPtr<int> ptr(new int[8]); - ptr.ClearAndDeleteArray(); - - // TODO(crbug.com/1346513): clang-format has a difficult time making - // sense of preprocessor arms mixed with designated initializers. - // - // clang-format off - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl> { - .wrap_raw_ptr_cnt = 1, - .release_wrapped_ptr_cnt = 1, - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 1, - .wrapped_ptr_swap_cnt = 0, - }), - CountersMatch()); - // clang-format on - EXPECT_EQ(ptr.get(), nullptr); -} - -TEST_F(RawPtrTest, ExtractAsDangling) { - CountingRawPtr<int> ptr(new int); - - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .wrap_raw_ptr_cnt = 1, - .release_wrapped_ptr_cnt = 0, - .get_for_dereference_cnt = 0, - .wrapped_ptr_swap_cnt = 0, - .wrap_raw_ptr_for_dup_cnt = 0, - .get_for_duplication_cnt = 0, - }), - CountersMatch()); - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingMayDangleImpl>{ - .wrap_raw_ptr_cnt = 0, - .release_wrapped_ptr_cnt = 0, - .get_for_dereference_cnt = 0, - .wrapped_ptr_swap_cnt = 0, - .wrap_raw_ptr_for_dup_cnt = 0, - .get_for_duplication_cnt = 0, - }), - CountersMatch()); - - EXPECT_TRUE(ptr.get()); - - CountingRawPtrMayDangle<int> dangling = ptr.ExtractAsDangling(); - - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .wrap_raw_ptr_cnt = 1, - .release_wrapped_ptr_cnt = 1, - .get_for_dereference_cnt = 0, - .wrapped_ptr_swap_cnt = 0, - .wrap_raw_ptr_for_dup_cnt = 0, - .get_for_duplication_cnt = 1, - }), - CountersMatch()); - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingMayDangleImpl>{ - .wrap_raw_ptr_cnt = 0, - .release_wrapped_ptr_cnt = 0, - .get_for_dereference_cnt = 0, - .wrapped_ptr_swap_cnt = 0, - .wrap_raw_ptr_for_dup_cnt = 1, - .get_for_duplication_cnt = 0, - }), - CountersMatch()); - - EXPECT_FALSE(ptr.get()); - EXPECT_TRUE(dangling.get()); - - dangling.ClearAndDelete(); -} - -TEST_F(RawPtrTest, ExtractAsDanglingFromDangling) { - CountingRawPtrMayDangle<int> ptr(new int); - - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingMayDangleImpl>{ - .wrap_raw_ptr_cnt = 1, - .release_wrapped_ptr_cnt = 0, - .get_for_dereference_cnt = 0, - .wrapped_ptr_swap_cnt = 0, - .wrap_raw_ptr_for_dup_cnt = 0, - .get_for_duplication_cnt = 0, - }), - CountersMatch()); - - CountingRawPtrMayDangle<int> dangling = ptr.ExtractAsDangling(); - - // wrap_raw_ptr_cnt remains `1` because, as `ptr` is already a dangling - // pointer, we are only moving `ptr` to `dangling` here to avoid extra cost. - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingMayDangleImpl>{ - .wrap_raw_ptr_cnt = 1, - .release_wrapped_ptr_cnt = 1, - .get_for_dereference_cnt = 0, - .wrapped_ptr_swap_cnt = 0, - .wrap_raw_ptr_for_dup_cnt = 0, - .get_for_duplication_cnt = 0, - }), - CountersMatch()); - - dangling.ClearAndDelete(); -} - -TEST_F(RawPtrTest, ConstVolatileVoidPtr) { - int32_t foo[] = {1234567890}; - CountingRawPtr<const volatile void> ptr = foo; - EXPECT_EQ(*static_cast<const volatile int32_t*>(ptr), 1234567890); - // Because we're using a cast, the extraction API kicks in, which doesn't - // know if the extracted pointer will be dereferenced or not. - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 1, - .get_for_comparison_cnt = 0, - }), - CountersMatch()); -} - -TEST_F(RawPtrTest, VoidPtr) { - int32_t foo[] = {1234567890}; - CountingRawPtr<void> ptr = foo; - EXPECT_EQ(*static_cast<int32_t*>(ptr), 1234567890); - // Because we're using a cast, the extraction API kicks in, which doesn't - // know if the extracted pointer will be dereferenced or not. - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 1, - .get_for_comparison_cnt = 0, - }), - CountersMatch()); -} - -TEST_F(RawPtrTest, OperatorEQ) { - int foo; - CountingRawPtr<int> ptr1 = nullptr; - EXPECT_TRUE(ptr1 == ptr1); - - CountingRawPtr<int> ptr2 = nullptr; - EXPECT_TRUE(ptr1 == ptr2); - - CountingRawPtr<int> ptr3 = &foo; - EXPECT_TRUE(&foo == ptr3); - EXPECT_TRUE(ptr3 == &foo); - EXPECT_FALSE(ptr1 == ptr3); - - ptr1 = &foo; - EXPECT_TRUE(ptr1 == ptr3); - EXPECT_TRUE(ptr3 == ptr1); - - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = 12, - }), - CountersMatch()); -} - -TEST_F(RawPtrTest, OperatorNE) { - int foo; - CountingRawPtr<int> ptr1 = nullptr; - EXPECT_FALSE(ptr1 != ptr1); - - CountingRawPtr<int> ptr2 = nullptr; - EXPECT_FALSE(ptr1 != ptr2); - - CountingRawPtr<int> ptr3 = &foo; - EXPECT_FALSE(&foo != ptr3); - EXPECT_FALSE(ptr3 != &foo); - EXPECT_TRUE(ptr1 != ptr3); - - ptr1 = &foo; - EXPECT_FALSE(ptr1 != ptr3); - EXPECT_FALSE(ptr3 != ptr1); - - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = 12, - }), - CountersMatch()); -} - -TEST_F(RawPtrTest, OperatorEQCast) { - int foo = 42; - const int* raw_int_ptr = &foo; - volatile void* raw_void_ptr = &foo; - CountingRawPtr<volatile int> checked_int_ptr = &foo; - CountingRawPtr<const void> checked_void_ptr = &foo; - EXPECT_TRUE(checked_int_ptr == checked_int_ptr); - EXPECT_TRUE(checked_int_ptr == raw_int_ptr); - EXPECT_TRUE(raw_int_ptr == checked_int_ptr); - EXPECT_TRUE(checked_void_ptr == checked_void_ptr); - EXPECT_TRUE(checked_void_ptr == raw_void_ptr); - EXPECT_TRUE(raw_void_ptr == checked_void_ptr); - EXPECT_TRUE(checked_int_ptr == checked_void_ptr); - EXPECT_TRUE(checked_int_ptr == raw_void_ptr); - EXPECT_TRUE(raw_int_ptr == checked_void_ptr); - EXPECT_TRUE(checked_void_ptr == checked_int_ptr); - EXPECT_TRUE(checked_void_ptr == raw_int_ptr); - EXPECT_TRUE(raw_void_ptr == checked_int_ptr); - // Make sure that all cases are handled by operator== (faster) and none by the - // cast operator (slower). - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = 16, - }), - CountersMatch()); -} - -TEST_F(RawPtrTest, OperatorEQCastHierarchy) { - Derived derived_val(42, 84, 1024); - Derived* raw_derived_ptr = &derived_val; - const Base1* raw_base1_ptr = &derived_val; - volatile Base2* raw_base2_ptr = &derived_val; - // Double check the basic understanding of pointers: Even though the numeric - // value (i.e. the address) isn't equal, the pointers are still equal. That's - // because from derived to base adjusts the address. - // raw_ptr must behave the same, which is checked below. - ASSERT_NE(reinterpret_cast<uintptr_t>(raw_base2_ptr), - reinterpret_cast<uintptr_t>(raw_derived_ptr)); - ASSERT_TRUE(raw_base2_ptr == raw_derived_ptr); - - CountingRawPtr<const volatile Derived> checked_derived_ptr = &derived_val; - CountingRawPtr<volatile Base1> checked_base1_ptr = &derived_val; - CountingRawPtr<const Base2> checked_base2_ptr = &derived_val; - EXPECT_TRUE(checked_derived_ptr == checked_derived_ptr); - EXPECT_TRUE(checked_derived_ptr == raw_derived_ptr); - EXPECT_TRUE(raw_derived_ptr == checked_derived_ptr); - EXPECT_TRUE(checked_derived_ptr == checked_base1_ptr); - EXPECT_TRUE(checked_derived_ptr == raw_base1_ptr); - EXPECT_TRUE(raw_derived_ptr == checked_base1_ptr); - EXPECT_TRUE(checked_base1_ptr == checked_derived_ptr); - EXPECT_TRUE(checked_base1_ptr == raw_derived_ptr); - EXPECT_TRUE(raw_base1_ptr == checked_derived_ptr); - // |base2_ptr| points to the second base class of |derived|, so will be - // located at an offset. While the stored raw uinptr_t values shouldn't match, - // ensure that the internal pointer manipulation correctly offsets when - // casting up and down the class hierarchy. - EXPECT_NE(reinterpret_cast<uintptr_t>(checked_base2_ptr.get()), - reinterpret_cast<uintptr_t>(checked_derived_ptr.get())); - EXPECT_NE(reinterpret_cast<uintptr_t>(raw_base2_ptr), - reinterpret_cast<uintptr_t>(checked_derived_ptr.get())); - EXPECT_NE(reinterpret_cast<uintptr_t>(checked_base2_ptr.get()), - reinterpret_cast<uintptr_t>(raw_derived_ptr)); - EXPECT_TRUE(checked_derived_ptr == checked_base2_ptr); - EXPECT_TRUE(checked_derived_ptr == raw_base2_ptr); - EXPECT_TRUE(raw_derived_ptr == checked_base2_ptr); - EXPECT_TRUE(checked_base2_ptr == checked_derived_ptr); - EXPECT_TRUE(checked_base2_ptr == raw_derived_ptr); - EXPECT_TRUE(raw_base2_ptr == checked_derived_ptr); - // Make sure that all cases are handled by operator== (faster) and none by the - // cast operator (slower). - // The 4 extractions come from .get() checks, that compare raw addresses. - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 4, - .get_for_comparison_cnt = 20, - }), - CountersMatch()); -} - -TEST_F(RawPtrTest, OperatorNECast) { - int foo = 42; - volatile int* raw_int_ptr = &foo; - const void* raw_void_ptr = &foo; - CountingRawPtr<const int> checked_int_ptr = &foo; - CountingRawPtr<volatile void> checked_void_ptr = &foo; - EXPECT_FALSE(checked_int_ptr != checked_int_ptr); - EXPECT_FALSE(checked_int_ptr != raw_int_ptr); - EXPECT_FALSE(raw_int_ptr != checked_int_ptr); - EXPECT_FALSE(checked_void_ptr != checked_void_ptr); - EXPECT_FALSE(checked_void_ptr != raw_void_ptr); - EXPECT_FALSE(raw_void_ptr != checked_void_ptr); - EXPECT_FALSE(checked_int_ptr != checked_void_ptr); - EXPECT_FALSE(checked_int_ptr != raw_void_ptr); - EXPECT_FALSE(raw_int_ptr != checked_void_ptr); - EXPECT_FALSE(checked_void_ptr != checked_int_ptr); - EXPECT_FALSE(checked_void_ptr != raw_int_ptr); - EXPECT_FALSE(raw_void_ptr != checked_int_ptr); - // Make sure that all cases are handled by operator== (faster) and none by the - // cast operator (slower). - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = 16, - }), - CountersMatch()); -} - -TEST_F(RawPtrTest, OperatorNECastHierarchy) { - Derived derived_val(42, 84, 1024); - const Derived* raw_derived_ptr = &derived_val; - volatile Base1* raw_base1_ptr = &derived_val; - const Base2* raw_base2_ptr = &derived_val; - CountingRawPtr<volatile Derived> checked_derived_ptr = &derived_val; - CountingRawPtr<const Base1> checked_base1_ptr = &derived_val; - CountingRawPtr<const volatile Base2> checked_base2_ptr = &derived_val; - EXPECT_FALSE(checked_derived_ptr != checked_derived_ptr); - EXPECT_FALSE(checked_derived_ptr != raw_derived_ptr); - EXPECT_FALSE(raw_derived_ptr != checked_derived_ptr); - EXPECT_FALSE(checked_derived_ptr != checked_base1_ptr); - EXPECT_FALSE(checked_derived_ptr != raw_base1_ptr); - EXPECT_FALSE(raw_derived_ptr != checked_base1_ptr); - EXPECT_FALSE(checked_base1_ptr != checked_derived_ptr); - EXPECT_FALSE(checked_base1_ptr != raw_derived_ptr); - EXPECT_FALSE(raw_base1_ptr != checked_derived_ptr); - // |base2_ptr| points to the second base class of |derived|, so will be - // located at an offset. While the stored raw uinptr_t values shouldn't match, - // ensure that the internal pointer manipulation correctly offsets when - // casting up and down the class hierarchy. - EXPECT_NE(reinterpret_cast<uintptr_t>(checked_base2_ptr.get()), - reinterpret_cast<uintptr_t>(checked_derived_ptr.get())); - EXPECT_NE(reinterpret_cast<uintptr_t>(raw_base2_ptr), - reinterpret_cast<uintptr_t>(checked_derived_ptr.get())); - EXPECT_NE(reinterpret_cast<uintptr_t>(checked_base2_ptr.get()), - reinterpret_cast<uintptr_t>(raw_derived_ptr)); - EXPECT_FALSE(checked_derived_ptr != checked_base2_ptr); - EXPECT_FALSE(checked_derived_ptr != raw_base2_ptr); - EXPECT_FALSE(raw_derived_ptr != checked_base2_ptr); - EXPECT_FALSE(checked_base2_ptr != checked_derived_ptr); - EXPECT_FALSE(checked_base2_ptr != raw_derived_ptr); - EXPECT_FALSE(raw_base2_ptr != checked_derived_ptr); - // Make sure that all cases are handled by operator== (faster) and none by the - // cast operator (slower). - // The 4 extractions come from .get() checks, that compare raw addresses. - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 4, - .get_for_comparison_cnt = 20, - }), - CountersMatch()); -} - -TEST_F(RawPtrTest, Cast) { - Derived derived_val(42, 84, 1024); - raw_ptr<Derived> checked_derived_ptr = &derived_val; - Base1* raw_base1_ptr = checked_derived_ptr; - EXPECT_EQ(raw_base1_ptr->b1, 42); - Base2* raw_base2_ptr = checked_derived_ptr; - EXPECT_EQ(raw_base2_ptr->b2, 84); - - Derived* raw_derived_ptr = static_cast<Derived*>(raw_base1_ptr); - EXPECT_EQ(raw_derived_ptr->b1, 42); - EXPECT_EQ(raw_derived_ptr->b2, 84); - EXPECT_EQ(raw_derived_ptr->d, 1024); - raw_derived_ptr = static_cast<Derived*>(raw_base2_ptr); - EXPECT_EQ(raw_derived_ptr->b1, 42); - EXPECT_EQ(raw_derived_ptr->b2, 84); - EXPECT_EQ(raw_derived_ptr->d, 1024); - - raw_ptr<Base1> checked_base1_ptr = raw_derived_ptr; - EXPECT_EQ(checked_base1_ptr->b1, 42); - raw_ptr<Base2> checked_base2_ptr = raw_derived_ptr; - EXPECT_EQ(checked_base2_ptr->b2, 84); - - raw_ptr<Derived> checked_derived_ptr2 = - static_cast<Derived*>(checked_base1_ptr); - EXPECT_EQ(checked_derived_ptr2->b1, 42); - EXPECT_EQ(checked_derived_ptr2->b2, 84); - EXPECT_EQ(checked_derived_ptr2->d, 1024); - checked_derived_ptr2 = static_cast<Derived*>(checked_base2_ptr); - EXPECT_EQ(checked_derived_ptr2->b1, 42); - EXPECT_EQ(checked_derived_ptr2->b2, 84); - EXPECT_EQ(checked_derived_ptr2->d, 1024); - - const Derived* raw_const_derived_ptr = checked_derived_ptr2; - EXPECT_EQ(raw_const_derived_ptr->b1, 42); - EXPECT_EQ(raw_const_derived_ptr->b2, 84); - EXPECT_EQ(raw_const_derived_ptr->d, 1024); - - raw_ptr<const Derived> checked_const_derived_ptr = raw_const_derived_ptr; - EXPECT_EQ(checked_const_derived_ptr->b1, 42); - EXPECT_EQ(checked_const_derived_ptr->b2, 84); - EXPECT_EQ(checked_const_derived_ptr->d, 1024); - - const Derived* raw_const_derived_ptr2 = checked_const_derived_ptr; - EXPECT_EQ(raw_const_derived_ptr2->b1, 42); - EXPECT_EQ(raw_const_derived_ptr2->b2, 84); - EXPECT_EQ(raw_const_derived_ptr2->d, 1024); - - raw_ptr<const Derived> checked_const_derived_ptr2 = raw_derived_ptr; - EXPECT_EQ(checked_const_derived_ptr2->b1, 42); - EXPECT_EQ(checked_const_derived_ptr2->b2, 84); - EXPECT_EQ(checked_const_derived_ptr2->d, 1024); - - raw_ptr<const Derived> checked_const_derived_ptr3 = checked_derived_ptr2; - EXPECT_EQ(checked_const_derived_ptr3->b1, 42); - EXPECT_EQ(checked_const_derived_ptr3->b2, 84); - EXPECT_EQ(checked_const_derived_ptr3->d, 1024); - - volatile Derived* raw_volatile_derived_ptr = checked_derived_ptr2; - EXPECT_EQ(raw_volatile_derived_ptr->b1, 42); - EXPECT_EQ(raw_volatile_derived_ptr->b2, 84); - EXPECT_EQ(raw_volatile_derived_ptr->d, 1024); - - raw_ptr<volatile Derived> checked_volatile_derived_ptr = - raw_volatile_derived_ptr; - EXPECT_EQ(checked_volatile_derived_ptr->b1, 42); - EXPECT_EQ(checked_volatile_derived_ptr->b2, 84); - EXPECT_EQ(checked_volatile_derived_ptr->d, 1024); - - void* raw_void_ptr = checked_derived_ptr; - raw_ptr<void> checked_void_ptr = raw_derived_ptr; - raw_ptr<Derived> checked_derived_ptr3 = static_cast<Derived*>(raw_void_ptr); - raw_ptr<Derived> checked_derived_ptr4 = - static_cast<Derived*>(checked_void_ptr); - EXPECT_EQ(checked_derived_ptr3->b1, 42); - EXPECT_EQ(checked_derived_ptr3->b2, 84); - EXPECT_EQ(checked_derived_ptr3->d, 1024); - EXPECT_EQ(checked_derived_ptr4->b1, 42); - EXPECT_EQ(checked_derived_ptr4->b2, 84); - EXPECT_EQ(checked_derived_ptr4->d, 1024); -} - -TEST_F(RawPtrTest, UpcastConvertible) { - { - Derived derived_val(42, 84, 1024); - raw_ptr<Derived> checked_derived_ptr = &derived_val; - - raw_ptr<Base1> checked_base1_ptr(checked_derived_ptr); - EXPECT_EQ(checked_base1_ptr->b1, 42); - raw_ptr<Base2> checked_base2_ptr(checked_derived_ptr); - EXPECT_EQ(checked_base2_ptr->b2, 84); - - checked_base1_ptr = checked_derived_ptr; - EXPECT_EQ(checked_base1_ptr->b1, 42); - checked_base2_ptr = checked_derived_ptr; - EXPECT_EQ(checked_base2_ptr->b2, 84); - - EXPECT_EQ(checked_base1_ptr, checked_derived_ptr); - EXPECT_EQ(checked_base2_ptr, checked_derived_ptr); - } - - { - Derived derived_val(42, 84, 1024); - raw_ptr<Derived> checked_derived_ptr1 = &derived_val; - raw_ptr<Derived> checked_derived_ptr2 = &derived_val; - raw_ptr<Derived> checked_derived_ptr3 = &derived_val; - raw_ptr<Derived> checked_derived_ptr4 = &derived_val; - - raw_ptr<Base1> checked_base1_ptr(std::move(checked_derived_ptr1)); - EXPECT_EQ(checked_base1_ptr->b1, 42); - raw_ptr<Base2> checked_base2_ptr(std::move(checked_derived_ptr2)); - EXPECT_EQ(checked_base2_ptr->b2, 84); - - checked_base1_ptr = std::move(checked_derived_ptr3); - EXPECT_EQ(checked_base1_ptr->b1, 42); - checked_base2_ptr = std::move(checked_derived_ptr4); - EXPECT_EQ(checked_base2_ptr->b2, 84); - } -} - -TEST_F(RawPtrTest, UpcastNotConvertible) { - class Base {}; - class Derived : private Base {}; - class Unrelated {}; - EXPECT_FALSE((std::is_convertible<raw_ptr<Derived>, raw_ptr<Base>>::value)); - EXPECT_FALSE((std::is_convertible<raw_ptr<Unrelated>, raw_ptr<Base>>::value)); - EXPECT_FALSE((std::is_convertible<raw_ptr<Unrelated>, raw_ptr<void>>::value)); - EXPECT_FALSE((std::is_convertible<raw_ptr<void>, raw_ptr<Unrelated>>::value)); - EXPECT_FALSE( - (std::is_convertible<raw_ptr<int64_t>, raw_ptr<int32_t>>::value)); - EXPECT_FALSE( - (std::is_convertible<raw_ptr<int16_t>, raw_ptr<int32_t>>::value)); -} - -TEST_F(RawPtrTest, UpcastPerformance) { - { - Derived derived_val(42, 84, 1024); - CountingRawPtr<Derived> checked_derived_ptr = &derived_val; - CountingRawPtr<Base1> checked_base1_ptr(checked_derived_ptr); - CountingRawPtr<Base2> checked_base2_ptr(checked_derived_ptr); - checked_base1_ptr = checked_derived_ptr; - checked_base2_ptr = checked_derived_ptr; - } - - { - Derived derived_val(42, 84, 1024); - CountingRawPtr<Derived> checked_derived_ptr = &derived_val; - CountingRawPtr<Base1> checked_base1_ptr(std::move(checked_derived_ptr)); - CountingRawPtr<Base2> checked_base2_ptr(std::move(checked_derived_ptr)); - checked_base1_ptr = std::move(checked_derived_ptr); - checked_base2_ptr = std::move(checked_derived_ptr); - } - - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = 0, - }), - CountersMatch()); -} - -TEST_F(RawPtrTest, CustomSwap) { - int foo1, foo2; - CountingRawPtr<int> ptr1(&foo1); - CountingRawPtr<int> ptr2(&foo2); - // Recommended use pattern. - using std::swap; - swap(ptr1, ptr2); - EXPECT_EQ(ptr1.get(), &foo2); - EXPECT_EQ(ptr2.get(), &foo1); - EXPECT_EQ(RawPtrCountingImpl::wrapped_ptr_swap_cnt, 1); -} - -TEST_F(RawPtrTest, StdSwap) { - int foo1, foo2; - CountingRawPtr<int> ptr1(&foo1); - CountingRawPtr<int> ptr2(&foo2); - std::swap(ptr1, ptr2); - EXPECT_EQ(ptr1.get(), &foo2); - EXPECT_EQ(ptr2.get(), &foo1); - EXPECT_EQ(RawPtrCountingImpl::wrapped_ptr_swap_cnt, 0); -} - -TEST_F(RawPtrTest, PostIncrementOperator) { - std::vector<int> foo({42, 43, 44, 45}); - CountingRawPtr<int> ptr = &foo[0]; - for (int i = 0; i < 4; ++i) { - ASSERT_EQ(*ptr++, 42 + i); - } - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 4, - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = 0, - }), - CountersMatch()); -} - -TEST_F(RawPtrTest, PostDecrementOperator) { - std::vector<int> foo({42, 43, 44, 45}); - CountingRawPtr<int> ptr = &foo[3]; - // Avoid decrementing out of the slot holding the vector's backing store. - for (int i = 3; i > 0; --i) { - ASSERT_EQ(*ptr--, 42 + i); - } - ASSERT_EQ(*ptr, 42); - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 4, - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = 0, - }), - CountersMatch()); -} - -TEST_F(RawPtrTest, PreIncrementOperator) { - std::vector<int> foo({42, 43, 44, 45}); - CountingRawPtr<int> ptr = &foo[0]; - for (int i = 0; i < 4; ++i, ++ptr) { - ASSERT_EQ(*ptr, 42 + i); - } - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 4, - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = 0, - }), - CountersMatch()); -} - -TEST_F(RawPtrTest, PreDecrementOperator) { - std::vector<int> foo({42, 43, 44, 45}); - CountingRawPtr<int> ptr = &foo[3]; - // Avoid decrementing out of the slot holding the vector's backing store. - for (int i = 3; i > 0; --i, --ptr) { - ASSERT_EQ(*ptr, 42 + i); - } - ASSERT_EQ(*ptr, 42); - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 4, - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = 0, - }), - CountersMatch()); -} - -TEST_F(RawPtrTest, PlusEqualOperator) { - std::vector<int> foo({42, 43, 44, 45}); - CountingRawPtr<int> ptr = &foo[0]; - for (int i = 0; i < 4; i += 2, ptr += 2) { - ASSERT_EQ(*ptr, 42 + i); - } - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 2, - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = 0, - }), - CountersMatch()); -} - -TEST_F(RawPtrTest, PlusEqualOperatorTypes) { - std::vector<int> foo({42, 43, 44, 45}); - CountingRawPtr<int> ptr = &foo[0]; - ASSERT_EQ(*ptr, 42); - ptr += 2; // Positive literal. - ASSERT_EQ(*ptr, 44); - ptr -= 2; // Negative literal. - ASSERT_EQ(*ptr, 42); - ptr += ptrdiff_t{1}; // ptrdiff_t. - ASSERT_EQ(*ptr, 43); - ptr += size_t{2}; // size_t. - ASSERT_EQ(*ptr, 45); -} - -TEST_F(RawPtrTest, MinusEqualOperator) { - std::vector<int> foo({42, 43, 44, 45}); - CountingRawPtr<int> ptr = &foo[3]; - ASSERT_EQ(*ptr, 45); - ptr -= 2; - ASSERT_EQ(*ptr, 43); - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 2, - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = 0, - }), - CountersMatch()); -} - -TEST_F(RawPtrTest, MinusEqualOperatorTypes) { - int foo[] = {42, 43, 44, 45}; - CountingRawPtr<int> ptr = &foo[3]; - ASSERT_EQ(*ptr, 45); - ptr -= 2; // Positive literal. - ASSERT_EQ(*ptr, 43); - ptr -= -2; // Negative literal. - ASSERT_EQ(*ptr, 45); - ptr -= ptrdiff_t{2}; // ptrdiff_t. - ASSERT_EQ(*ptr, 43); - ptr -= size_t{1}; // size_t. - ASSERT_EQ(*ptr, 42); -} - -TEST_F(RawPtrTest, PlusOperator) { - int foo[] = {42, 43, 44, 45}; - CountingRawPtr<int> ptr = foo; - for (int i = 0; i < 4; ++i) { - ASSERT_EQ(*(ptr + i), 42 + i); - } - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 4, - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = 0, - }), - CountersMatch()); -} - -TEST_F(RawPtrTest, MinusOperator) { - int foo[] = {42, 43, 44, 45}; - CountingRawPtr<int> ptr = &foo[4]; - for (int i = 1; i <= 4; ++i) { - ASSERT_EQ(*(ptr - i), 46 - i); - } - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 4, - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = 0, - }), - CountersMatch()); -} - -TEST_F(RawPtrTest, MinusDeltaOperator) { - int foo[] = {42, 43, 44, 45}; - CountingRawPtr<int> ptrs[] = {&foo[0], &foo[1], &foo[2], &foo[3], &foo[4]}; - for (int i = 0; i <= 4; ++i) { - for (int j = 0; j <= 4; ++j) { - ASSERT_EQ(ptrs[i] - ptrs[j], i - j); - ASSERT_EQ(ptrs[i] - &foo[j], i - j); - ASSERT_EQ(&foo[i] - ptrs[j], i - j); - } - } - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = 0, - }), - CountersMatch()); -} - -TEST_F(RawPtrTest, AdvanceString) { - const char kChars[] = "Hello"; - std::string str = kChars; - CountingRawPtr<const char> ptr = str.c_str(); - for (size_t i = 0; i < str.size(); ++i, ++ptr) { - ASSERT_EQ(*ptr, kChars[i]); - } - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 5, - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = 0, - }), - CountersMatch()); -} - -TEST_F(RawPtrTest, AssignmentFromNullptr) { - CountingRawPtr<int> wrapped_ptr; - wrapped_ptr = nullptr; - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .wrap_raw_ptr_cnt = 0, - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = 0, - }), - CountersMatch()); -} - -void FunctionWithRawPtrParameter(raw_ptr<int> actual_ptr, int* expected_ptr) { - EXPECT_EQ(actual_ptr.get(), expected_ptr); - EXPECT_EQ(*actual_ptr, *expected_ptr); -} - -// This test checks that raw_ptr<T> can be passed by value into function -// parameters. This is mostly a smoke test for TRIVIAL_ABI attribute. -TEST_F(RawPtrTest, FunctionParameters_ImplicitlyMovedTemporary) { - int x = 123; - FunctionWithRawPtrParameter( - raw_ptr<int>(&x), // Temporary that will be moved into the function. - &x); -} - -// This test checks that raw_ptr<T> can be passed by value into function -// parameters. This is mostly a smoke test for TRIVIAL_ABI attribute. -TEST_F(RawPtrTest, FunctionParameters_ExplicitlyMovedLValue) { - int x = 123; - raw_ptr<int> ptr(&x); - FunctionWithRawPtrParameter(std::move(ptr), &x); -} - -// This test checks that raw_ptr<T> can be passed by value into function -// parameters. This is mostly a smoke test for TRIVIAL_ABI attribute. -TEST_F(RawPtrTest, FunctionParameters_Copy) { - int x = 123; - raw_ptr<int> ptr(&x); - FunctionWithRawPtrParameter(ptr, // `ptr` will be copied into the function. - &x); -} - -TEST_F(RawPtrTest, SetLookupUsesGetForComparison) { - int x = 123; - CountingRawPtr<int> ptr(&x); - std::set<CountingRawPtr<int>> set; - - RawPtrCountingImpl::ClearCounters(); - set.emplace(&x); - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .wrap_raw_ptr_cnt = 1, - // Nothing to compare to yet. - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = 0, - .wrapped_ptr_less_cnt = 0, - }), - CountersMatch()); - - RawPtrCountingImpl::ClearCounters(); - set.emplace(ptr); - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .wrap_raw_ptr_cnt = 0, - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 0, - // 2 items to compare to => 4 calls. - .get_for_comparison_cnt = 4, - // 1 element to compare to => 2 calls. - .wrapped_ptr_less_cnt = 2, - }), - CountersMatch()); - - RawPtrCountingImpl::ClearCounters(); - set.count(&x); - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .wrap_raw_ptr_cnt = 0, - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 0, - // 2 comparisons => 2 extractions. Less than before, because - // this time a raw pointer is one side of the comparison. - .get_for_comparison_cnt = 2, - // 2 items to compare to => 4 calls. - .wrapped_ptr_less_cnt = 2, - }), - CountersMatch()); - - RawPtrCountingImpl::ClearCounters(); - set.count(ptr); - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .wrap_raw_ptr_cnt = 0, - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 0, - // 2 comparisons => 4 extractions. - .get_for_comparison_cnt = 4, - // 2 items to compare to => 4 calls. - .wrapped_ptr_less_cnt = 2, - }), - CountersMatch()); -} - -TEST_F(RawPtrTest, ComparisonOperatorUsesGetForComparison) { - int x = 123; - CountingRawPtr<int> ptr(&x); - - RawPtrCountingImpl::ClearCounters(); - EXPECT_FALSE(ptr < ptr); - EXPECT_FALSE(ptr > ptr); - EXPECT_TRUE(ptr <= ptr); - EXPECT_TRUE(ptr >= ptr); - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .wrap_raw_ptr_cnt = 0, - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = 8, - // < is used directly, not std::less(). - .wrapped_ptr_less_cnt = 0, - }), - CountersMatch()); - - RawPtrCountingImpl::ClearCounters(); - EXPECT_FALSE(ptr < &x); - EXPECT_FALSE(ptr > &x); - EXPECT_TRUE(ptr <= &x); - EXPECT_TRUE(ptr >= &x); - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .wrap_raw_ptr_cnt = 0, - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = 4, - .wrapped_ptr_less_cnt = 0, - }), - CountersMatch()); - - RawPtrCountingImpl::ClearCounters(); - EXPECT_FALSE(&x < ptr); - EXPECT_FALSE(&x > ptr); - EXPECT_TRUE(&x <= ptr); - EXPECT_TRUE(&x >= ptr); - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .wrap_raw_ptr_cnt = 0, - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = 4, - .wrapped_ptr_less_cnt = 0, - }), - CountersMatch()); -} - -// Two `raw_ptr`s with different Traits should still hit `GetForComparison()` -// (as opposed to `GetForExtraction()`) in their comparison operators. We use -// `CountingRawPtr` and `CountingRawPtrMayDangle` to contrast two different -// Traits. -TEST_F(RawPtrTest, OperatorsUseGetForComparison) { - int x = 123; - CountingRawPtr<int> ptr1 = &x; - CountingRawPtrMayDangle<int> ptr2 = &x; - - RawPtrCountingImpl::ClearCounters(); - RawPtrCountingMayDangleImpl::ClearCounters(); - - EXPECT_TRUE(ptr1 == ptr2); - EXPECT_FALSE(ptr1 != ptr2); - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = 2, - }), - CountersMatch()); - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingMayDangleImpl>{ - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = 2, - }), - CountersMatch()); - - EXPECT_FALSE(ptr1 < ptr2); - EXPECT_FALSE(ptr1 > ptr2); - EXPECT_TRUE(ptr1 <= ptr2); - EXPECT_TRUE(ptr1 >= ptr2); - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = 6, - }), - CountersMatch()); - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingMayDangleImpl>{ - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = 6, - }), - CountersMatch()); -} - -// This test checks how the std library handles collections like -// std::vector<raw_ptr<T>>. -// -// When this test is written, reallocating std::vector's storage (e.g. -// when growing the vector) requires calling raw_ptr's destructor on the -// old storage (after std::move-ing the data to the new storage). In -// the future we hope that TRIVIAL_ABI (or [trivially_relocatable]] -// proposed by P1144 [1]) will allow memcpy-ing the elements into the -// new storage (without invoking destructors and move constructors -// and/or move assignment operators). At that point, the assert in the -// test should be modified to capture the new, better behavior. -// -// In the meantime, this test serves as a basic correctness test that -// ensures that raw_ptr<T> stored in a std::vector passes basic smoke -// tests. -// -// [1] -// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1144r5.html#wording-attribute -TEST_F(RawPtrTest, TrivialRelocability) { - std::vector<CountingRawPtr<int>> vector; - int x = 123; - - // See how many times raw_ptr's destructor is called when std::vector - // needs to increase its capacity and reallocate the internal vector - // storage (moving the raw_ptr elements). - RawPtrCountingImpl::ClearCounters(); - size_t number_of_capacity_changes = 0; - do { - size_t previous_capacity = vector.capacity(); - while (vector.capacity() == previous_capacity) { - vector.emplace_back(&x); - } - number_of_capacity_changes++; - } while (number_of_capacity_changes < 10); -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) || \ - BUILDFLAG(USE_ASAN_UNOWNED_PTR) || BUILDFLAG(USE_HOOKABLE_RAW_PTR) - // TODO(lukasza): In the future (once C++ language and std library - // support custom trivially relocatable objects) this #if branch can - // be removed (keeping only the right long-term expectation from the - // #else branch). - EXPECT_NE(0, RawPtrCountingImpl::release_wrapped_ptr_cnt); -#else - // This is the right long-term expectation. - // - // (This EXPECT_EQ assertion is slightly misleading in - // !USE_BACKUP_REF_PTR mode, because RawPtrNoOpImpl has a default - // destructor that doesn't go through - // RawPtrCountingImpl::ReleaseWrappedPtr. Nevertheless, the spirit of - // the EXPECT_EQ is correct + the assertion should be true in the - // long-term.) - EXPECT_EQ(0, RawPtrCountingImpl::release_wrapped_ptr_cnt); -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) || - // BUILDFLAG(USE_ASAN_UNOWNED_PTR) - - // Basic smoke test that raw_ptr elements in a vector work okay. - for (const auto& elem : vector) { - EXPECT_EQ(elem.get(), &x); - EXPECT_EQ(*elem, x); - } - - // Verification that release_wrapped_ptr_cnt does capture how many times the - // destructors are called (e.g. that it is not always zero). - RawPtrCountingImpl::ClearCounters(); - size_t number_of_cleared_elements = vector.size(); - vector.clear(); -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) || \ - BUILDFLAG(USE_ASAN_UNOWNED_PTR) || BUILDFLAG(USE_HOOKABLE_RAW_PTR) - - EXPECT_EQ((int)number_of_cleared_elements, - RawPtrCountingImpl::release_wrapped_ptr_cnt); -#else - // TODO(lukasza): !USE_BACKUP_REF_PTR / RawPtrNoOpImpl has a default - // destructor that doesn't go through - // RawPtrCountingImpl::ReleaseWrappedPtr. So we can't really depend - // on `g_release_wrapped_ptr_cnt`. This #else branch should be - // deleted once USE_BACKUP_REF_PTR is removed (e.g. once BackupRefPtr - // ships to the Stable channel). - EXPECT_EQ(0, RawPtrCountingImpl::release_wrapped_ptr_cnt); - std::ignore = number_of_cleared_elements; -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) || - // BUILDFLAG(USE_ASAN_UNOWNED_PTR) -} - -struct BaseStruct { - explicit BaseStruct(int a) : a(a) {} - virtual ~BaseStruct() = default; - - int a; -}; - -struct DerivedType1 : public BaseStruct { - explicit DerivedType1(int a, int b) : BaseStruct(a), b(b) {} - int b; -}; - -struct DerivedType2 : public BaseStruct { - explicit DerivedType2(int a, int c) : BaseStruct(a), c(c) {} - int c; -}; - -TEST_F(RawPtrTest, DerivedStructsComparison) { - DerivedType1 derived_1(42, 84); - raw_ptr<DerivedType1> checked_derived1_ptr = &derived_1; - DerivedType2 derived_2(21, 10); - raw_ptr<DerivedType2> checked_derived2_ptr = &derived_2; - - // Make sure that comparing a |DerivedType2*| to a |DerivedType1*| casted - // as a |BaseStruct*| doesn't cause CFI errors. - EXPECT_NE(checked_derived1_ptr, - static_cast<BaseStruct*>(checked_derived2_ptr.get())); - EXPECT_NE(static_cast<BaseStruct*>(checked_derived1_ptr.get()), - checked_derived2_ptr); -} - -class PmfTestBase { - public: - int MemFunc(char, double) const { return 11; } -}; - -class PmfTestDerived : public PmfTestBase { - public: - using PmfTestBase::MemFunc; - int MemFunc(float, double) { return 22; } -}; - -TEST_F(RawPtrTest, WorksWithOptional) { - int x = 0; - absl::optional<raw_ptr<int>> maybe_int; - EXPECT_FALSE(maybe_int.has_value()); - - maybe_int = nullptr; - ASSERT_TRUE(maybe_int.has_value()); - EXPECT_EQ(nullptr, maybe_int.value()); - - maybe_int = &x; - ASSERT_TRUE(maybe_int.has_value()); - EXPECT_EQ(&x, maybe_int.value()); -} - -TEST_F(RawPtrTest, WorksWithVariant) { - int x = 100; - absl::variant<int, raw_ptr<int>> vary; - ASSERT_EQ(0u, vary.index()); - EXPECT_EQ(0, absl::get<int>(vary)); - - vary = x; - ASSERT_EQ(0u, vary.index()); - EXPECT_EQ(100, absl::get<int>(vary)); - - vary = nullptr; - ASSERT_EQ(1u, vary.index()); - EXPECT_EQ(nullptr, absl::get<raw_ptr<int>>(vary)); - - vary = &x; - ASSERT_EQ(1u, vary.index()); - EXPECT_EQ(&x, absl::get<raw_ptr<int>>(vary)); -} - -TEST_F(RawPtrTest, CrossKindConversion) { - int x = 123; - CountingRawPtr<int> ptr1 = &x; - - RawPtrCountingImpl::ClearCounters(); - RawPtrCountingMayDangleImpl::ClearCounters(); - - CountingRawPtrMayDangle<int> ptr2(ptr1); - CountingRawPtrMayDangle<int> ptr3(std::move(ptr1)); // Falls back to copy. - - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 0, - .get_for_duplication_cnt = 2}), - CountersMatch()); - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingMayDangleImpl>{ - .wrap_raw_ptr_cnt = 0, .wrap_raw_ptr_for_dup_cnt = 2}), - CountersMatch()); -} - -TEST_F(RawPtrTest, CrossKindAssignment) { - int x = 123; - CountingRawPtr<int> ptr1 = &x; - - RawPtrCountingImpl::ClearCounters(); - RawPtrCountingMayDangleImpl::ClearCounters(); - - CountingRawPtrMayDangle<int> ptr2; - CountingRawPtrMayDangle<int> ptr3; - ptr2 = ptr1; - ptr3 = std::move(ptr1); // Falls back to copy. - - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 0, - .get_for_duplication_cnt = 2}), - CountersMatch()); - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingMayDangleImpl>{ - .wrap_raw_ptr_cnt = 0, .wrap_raw_ptr_for_dup_cnt = 2}), - CountersMatch()); -} - -// Without the explicitly customized `raw_ptr::to_address()`, -// `base::to_address()` will use the dereference operator. This is not -// what we want; this test enforces extraction semantics for -// `to_address()`. -TEST_F(RawPtrTest, ToAddressDoesNotDereference) { - CountingRawPtr<int> ptr = nullptr; - int* raw = base::to_address(ptr); - std::ignore = raw; - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 1, - .get_for_comparison_cnt = 0, - .get_for_duplication_cnt = 0}), - CountersMatch()); -} - -TEST_F(RawPtrTest, ToAddressGivesBackRawAddress) { - int* raw = nullptr; - raw_ptr<int> miracle = raw; - EXPECT_EQ(base::to_address(raw), base::to_address(miracle)); -} - -// Verifies that `raw_ptr_experimental` is aliased appropriately. -// -// The `DisableDanglingPtrDetection` trait is arbitrarily chosen and is -// just there to ensure that `raw_ptr_experimental` knows how to field -// the traits template argument. -#if BUILDFLAG(ENABLE_RAW_PTR_EXPERIMENTAL) -static_assert( - std::is_same_v<raw_ptr_experimental<int, DisableDanglingPtrDetection>, - raw_ptr<int, DisableDanglingPtrDetection>>); -static_assert( - std::is_same_v<raw_ptr_experimental<const int, DisableDanglingPtrDetection>, - raw_ptr<const int, DisableDanglingPtrDetection>>); -static_assert( - std::is_same_v< - const raw_ptr_experimental<const int, DisableDanglingPtrDetection>, - const raw_ptr<const int, DisableDanglingPtrDetection>>); -#else // BUILDFLAG(ENABLE_RAW_PTR_EXPERIMENTAL) -// `DisableDanglingPtrDetection` means nothing here and is silently -// ignored. -static_assert( - std::is_same_v<raw_ptr_experimental<int, DisableDanglingPtrDetection>, - int*>); -static_assert( - std::is_same_v<raw_ptr_experimental<const int, DisableDanglingPtrDetection>, - const int*>); -static_assert( - std::is_same_v< - const raw_ptr_experimental<const int, DisableDanglingPtrDetection>, - const int* const>); -#endif // BUILDFLAG(ENABLE_RAW_PTR_EXPERIMENTAL) - -} // namespace - -namespace base { -namespace internal { - -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) && \ - !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - -void HandleOOM(size_t unused_size) { - LOG(FATAL) << "Out of memory"; -} - -static constexpr partition_alloc::PartitionOptions kOpts = { - partition_alloc::PartitionOptions::AlignedAlloc::kDisallowed, - partition_alloc::PartitionOptions::ThreadCache::kDisabled, - partition_alloc::PartitionOptions::Quarantine::kDisallowed, - partition_alloc::PartitionOptions::Cookie::kAllowed, - partition_alloc::PartitionOptions::BackupRefPtr::kEnabled, - partition_alloc::PartitionOptions::BackupRefPtrZapping::kEnabled, - partition_alloc::PartitionOptions::UseConfigurablePool::kNo, -}; - -class BackupRefPtrTest : public testing::Test { - protected: - void SetUp() override { - // TODO(bartekn): Avoid using PartitionAlloc API directly. Switch to - // new/delete once PartitionAlloc Everywhere is fully enabled. - partition_alloc::PartitionAllocGlobalInit(HandleOOM); - allocator_.init(kOpts); - } - - partition_alloc::PartitionAllocator allocator_; -}; - -TEST_F(BackupRefPtrTest, Basic) { - base::CPU cpu; - - int* raw_ptr1 = - reinterpret_cast<int*>(allocator_.root()->Alloc(sizeof(int), "")); - // Use the actual raw_ptr implementation, not a test substitute, to - // exercise real PartitionAlloc paths. - raw_ptr<int> wrapped_ptr1 = raw_ptr1; - - *raw_ptr1 = 42; - EXPECT_EQ(*raw_ptr1, *wrapped_ptr1); - - allocator_.root()->Free(raw_ptr1); -#if DCHECK_IS_ON() || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) - // In debug builds, the use-after-free should be caught immediately. - EXPECT_DEATH_IF_SUPPORTED(g_volatile_int_to_ignore = *wrapped_ptr1, ""); -#else // DCHECK_IS_ON() || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) - if (cpu.has_mte()) { - // If the hardware supports MTE, the use-after-free should also be caught. - EXPECT_DEATH_IF_SUPPORTED(g_volatile_int_to_ignore = *wrapped_ptr1, ""); - } else { - // The allocation should be poisoned since there's a raw_ptr alive. - EXPECT_NE(*wrapped_ptr1, 42); - } - - // The allocator should not be able to reuse the slot at this point. - void* raw_ptr2 = allocator_.root()->Alloc(sizeof(int), ""); - EXPECT_NE(partition_alloc::UntagPtr(raw_ptr1), - partition_alloc::UntagPtr(raw_ptr2)); - allocator_.root()->Free(raw_ptr2); - - // When the last reference is released, the slot should become reusable. - wrapped_ptr1 = nullptr; - void* raw_ptr3 = allocator_.root()->Alloc(sizeof(int), ""); - EXPECT_EQ(partition_alloc::UntagPtr(raw_ptr1), - partition_alloc::UntagPtr(raw_ptr3)); - allocator_.root()->Free(raw_ptr3); -#endif // DCHECK_IS_ON() || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) -} - -TEST_F(BackupRefPtrTest, ZeroSized) { - std::vector<raw_ptr<void>> ptrs; - // Use a reasonable number of elements to fill up the slot span. - for (int i = 0; i < 128 * 1024; ++i) { - // Constructing a raw_ptr instance from a zero-sized allocation should - // not result in a crash. - ptrs.emplace_back(allocator_.root()->Alloc(0, "")); - } -} - -TEST_F(BackupRefPtrTest, EndPointer) { - // This test requires a fresh partition with an empty free list. - // Check multiple size buckets and levels of slot filling. - for (int size = 0; size < 1024; size += sizeof(void*)) { - // Creating a raw_ptr from an address right past the end of an allocation - // should not result in a crash or corrupt the free list. - char* raw_ptr1 = - reinterpret_cast<char*>(allocator_.root()->Alloc(size, "")); - raw_ptr<char, AllowPtrArithmetic> wrapped_ptr = raw_ptr1 + size; - wrapped_ptr = nullptr; - // We need to make two more allocations to turn the possible free list - // corruption into an observable crash. - char* raw_ptr2 = - reinterpret_cast<char*>(allocator_.root()->Alloc(size, "")); - char* raw_ptr3 = - reinterpret_cast<char*>(allocator_.root()->Alloc(size, "")); - - // Similarly for operator+=. - char* raw_ptr4 = - reinterpret_cast<char*>(allocator_.root()->Alloc(size, "")); - wrapped_ptr = raw_ptr4; - wrapped_ptr += size; - wrapped_ptr = nullptr; - char* raw_ptr5 = - reinterpret_cast<char*>(allocator_.root()->Alloc(size, "")); - char* raw_ptr6 = - reinterpret_cast<char*>(allocator_.root()->Alloc(size, "")); - - allocator_.root()->Free(raw_ptr1); - allocator_.root()->Free(raw_ptr2); - allocator_.root()->Free(raw_ptr3); - allocator_.root()->Free(raw_ptr4); - allocator_.root()->Free(raw_ptr5); - allocator_.root()->Free(raw_ptr6); - } -} - -TEST_F(BackupRefPtrTest, QuarantinedBytes) { - uint64_t* raw_ptr1 = reinterpret_cast<uint64_t*>( - allocator_.root()->Alloc(sizeof(uint64_t), "")); - raw_ptr<uint64_t> wrapped_ptr1 = raw_ptr1; - EXPECT_EQ(allocator_.root()->total_size_of_brp_quarantined_bytes.load( - std::memory_order_relaxed), - 0U); - EXPECT_EQ(allocator_.root()->total_count_of_brp_quarantined_slots.load( - std::memory_order_relaxed), - 0U); - - // Memory should get quarantined. - allocator_.root()->Free(raw_ptr1); - EXPECT_GT(allocator_.root()->total_size_of_brp_quarantined_bytes.load( - std::memory_order_relaxed), - 0U); - EXPECT_EQ(allocator_.root()->total_count_of_brp_quarantined_slots.load( - std::memory_order_relaxed), - 1U); - - // Non quarantined free should not effect total_size_of_brp_quarantined_bytes - void* raw_ptr2 = allocator_.root()->Alloc(sizeof(uint64_t), ""); - allocator_.root()->Free(raw_ptr2); - - // Freeing quarantined memory should bring the size back down to zero. - wrapped_ptr1 = nullptr; - EXPECT_EQ(allocator_.root()->total_size_of_brp_quarantined_bytes.load( - std::memory_order_relaxed), - 0U); - EXPECT_EQ(allocator_.root()->total_count_of_brp_quarantined_slots.load( - std::memory_order_relaxed), - 0U); -} - -void RunBackupRefPtrImplAdvanceTest( - partition_alloc::PartitionAllocator& allocator, - size_t requested_size) { - char* ptr = static_cast<char*>(allocator.root()->Alloc(requested_size, "")); - raw_ptr<char, AllowPtrArithmetic> protected_ptr = ptr; - protected_ptr += 123; - protected_ptr -= 123; - protected_ptr = protected_ptr + 123; - protected_ptr = protected_ptr - 123; - protected_ptr += requested_size / 2; - // end-of-allocation address should not cause an error immediately, but it may - // result in the pointer being poisoned. - protected_ptr = protected_ptr + requested_size / 2; -#if BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR) - EXPECT_DEATH_IF_SUPPORTED(*protected_ptr = ' ', ""); - protected_ptr -= 1; // This brings the pointer back within - // bounds, which causes the poison to be removed. - *protected_ptr = ' '; - protected_ptr += 1; // Reposition pointer back past end of allocation. -#endif - EXPECT_CHECK_DEATH(protected_ptr = protected_ptr + 1); - EXPECT_CHECK_DEATH(protected_ptr += 1); - EXPECT_CHECK_DEATH(++protected_ptr); - - // Even though |protected_ptr| is already pointing to the end of the - // allocation, assign it explicitly to make sure the underlying implementation - // doesn't "switch" to the next slot. - protected_ptr = ptr + requested_size; - protected_ptr -= requested_size / 2; - protected_ptr = protected_ptr - requested_size / 2; - EXPECT_CHECK_DEATH(protected_ptr = protected_ptr - 1); - EXPECT_CHECK_DEATH(protected_ptr -= 1); - EXPECT_CHECK_DEATH(--protected_ptr); - -#if BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR) - // An array type that should be more than a third the size of the available - // memory for the allocation such that incrementing a pointer to this type - // twice causes it to point to a memory location that is too small to fit a - // complete element of this type. - typedef int OverThirdArray[200 / sizeof(int)]; - raw_ptr<OverThirdArray> protected_arr_ptr = - reinterpret_cast<OverThirdArray*>(ptr); - - protected_arr_ptr++; - **protected_arr_ptr = 4; - protected_arr_ptr++; - EXPECT_DEATH_IF_SUPPORTED(** protected_arr_ptr = 4, ""); -#endif // BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR) - - allocator.root()->Free(ptr); -} - -TEST_F(BackupRefPtrTest, Advance) { - // This requires some internal PartitionAlloc knowledge, but for the test to - // work well the allocation + extras have to fill out the entire slot. That's - // because PartitionAlloc doesn't know exact allocation size and bases the - // guards on the slot size. - // - // A power of two is a safe choice for a slot size, then adjust it for extras. - size_t slot_size = 512; - size_t requested_size = - allocator_.root()->AdjustSizeForExtrasSubtract(slot_size); - // Verify that we're indeed filling up the slot. - ASSERT_EQ( - requested_size, - allocator_.root()->AllocationCapacityFromRequestedSize(requested_size)); - RunBackupRefPtrImplAdvanceTest(allocator_, requested_size); - - // We don't have the same worry for single-slot spans, as PartitionAlloc knows - // exactly where the allocation ends. - size_t raw_size = 300003; - ASSERT_GT(raw_size, partition_alloc::internal::MaxRegularSlotSpanSize()); - ASSERT_LE(raw_size, partition_alloc::internal::kMaxBucketed); - requested_size = allocator_.root()->AdjustSizeForExtrasSubtract(slot_size); - RunBackupRefPtrImplAdvanceTest(allocator_, requested_size); - - // Same for direct map. - raw_size = 1001001; - ASSERT_GT(raw_size, partition_alloc::internal::kMaxBucketed); - requested_size = allocator_.root()->AdjustSizeForExtrasSubtract(slot_size); - RunBackupRefPtrImplAdvanceTest(allocator_, requested_size); -} - -TEST_F(BackupRefPtrTest, AdvanceAcrossPools) { - char array1[1000]; - char array2[1000]; - - char* in_pool_ptr = static_cast<char*>(allocator_.root()->Alloc(123, "")); - - raw_ptr<char, AllowPtrArithmetic> protected_ptr = array1; - // Nothing bad happens. Both pointers are outside of the BRP pool, so no - // checks are triggered. - protected_ptr += (array2 - array1); - // A pointer is shifted from outside of the BRP pool into the BRP pool. This - // should trigger death to avoid - EXPECT_CHECK_DEATH(protected_ptr += (in_pool_ptr - array2)); - - protected_ptr = in_pool_ptr; - // Same when a pointer is shifted from inside the BRP pool out of it. - EXPECT_CHECK_DEATH(protected_ptr += (array1 - in_pool_ptr)); - - allocator_.root()->Free(in_pool_ptr); -} - -TEST_F(BackupRefPtrTest, GetDeltaElems) { - size_t requested_size = allocator_.root()->AdjustSizeForExtrasSubtract(512); - char* ptr1 = static_cast<char*>(allocator_.root()->Alloc(requested_size, "")); - char* ptr2 = static_cast<char*>(allocator_.root()->Alloc(requested_size, "")); - ASSERT_LT(ptr1, ptr2); // There should be a ref-count between slots. - raw_ptr<char> protected_ptr1 = ptr1; - raw_ptr<char> protected_ptr1_2 = ptr1 + 1; - raw_ptr<char> protected_ptr1_3 = ptr1 + requested_size - 1; - raw_ptr<char> protected_ptr1_4 = ptr1 + requested_size; - raw_ptr<char> protected_ptr2 = ptr2; - raw_ptr<char> protected_ptr2_2 = ptr2 + 1; - - EXPECT_EQ(protected_ptr1_2 - protected_ptr1, 1); - EXPECT_EQ(protected_ptr1 - protected_ptr1_2, -1); - EXPECT_EQ(protected_ptr1_3 - protected_ptr1, - checked_cast<ptrdiff_t>(requested_size) - 1); - EXPECT_EQ(protected_ptr1 - protected_ptr1_3, - -checked_cast<ptrdiff_t>(requested_size) + 1); - EXPECT_EQ(protected_ptr1_4 - protected_ptr1, - checked_cast<ptrdiff_t>(requested_size)); - EXPECT_EQ(protected_ptr1 - protected_ptr1_4, - -checked_cast<ptrdiff_t>(requested_size)); -#if BUILDFLAG(ENABLE_POINTER_SUBTRACTION_CHECK) - EXPECT_CHECK_DEATH(protected_ptr2 - protected_ptr1); - EXPECT_CHECK_DEATH(protected_ptr1 - protected_ptr2); - EXPECT_CHECK_DEATH(protected_ptr2 - protected_ptr1_4); - EXPECT_CHECK_DEATH(protected_ptr1_4 - protected_ptr2); - EXPECT_CHECK_DEATH(protected_ptr2_2 - protected_ptr1); - EXPECT_CHECK_DEATH(protected_ptr1 - protected_ptr2_2); - EXPECT_CHECK_DEATH(protected_ptr2_2 - protected_ptr1_4); - EXPECT_CHECK_DEATH(protected_ptr1_4 - protected_ptr2_2); -#endif // BUILDFLAG(ENABLE_POINTER_SUBTRACTION_CHECK) - EXPECT_EQ(protected_ptr2_2 - protected_ptr2, 1); - EXPECT_EQ(protected_ptr2 - protected_ptr2_2, -1); - - allocator_.root()->Free(ptr1); - allocator_.root()->Free(ptr2); -} - -bool IsQuarantineEmpty(partition_alloc::PartitionAllocator& allocator) { - return allocator.root()->total_size_of_brp_quarantined_bytes.load( - std::memory_order_relaxed) == 0; -} - -struct BoundRawPtrTestHelper { - static BoundRawPtrTestHelper* Create( - partition_alloc::PartitionAllocator& allocator) { - return new (allocator.root()->Alloc(sizeof(BoundRawPtrTestHelper), "")) - BoundRawPtrTestHelper(allocator); - } - - explicit BoundRawPtrTestHelper(partition_alloc::PartitionAllocator& allocator) - : owning_allocator(allocator), - once_callback( - BindOnce(&BoundRawPtrTestHelper::DeleteItselfAndCheckIfInQuarantine, - Unretained(this))), - repeating_callback(BindRepeating( - &BoundRawPtrTestHelper::DeleteItselfAndCheckIfInQuarantine, - Unretained(this))) {} - - void DeleteItselfAndCheckIfInQuarantine() { - auto& allocator = *owning_allocator; - EXPECT_TRUE(IsQuarantineEmpty(allocator)); - - // Since we use a non-default partition, `delete` has to be simulated. - this->~BoundRawPtrTestHelper(); - allocator.root()->Free(this); - - EXPECT_FALSE(IsQuarantineEmpty(allocator)); - } - - const raw_ref<partition_alloc::PartitionAllocator> owning_allocator; - OnceClosure once_callback; - RepeatingClosure repeating_callback; -}; - -// Check that bound callback arguments remain protected by BRP for the -// entire duration of a callback invocation. -TEST_F(BackupRefPtrTest, Bind) { - // This test requires a separate partition; otherwise, unrelated allocations - // might interfere with `IsQuarantineEmpty`. - auto* object_for_once_callback1 = BoundRawPtrTestHelper::Create(allocator_); - std::move(object_for_once_callback1->once_callback).Run(); - EXPECT_TRUE(IsQuarantineEmpty(allocator_)); - - auto* object_for_repeating_callback1 = - BoundRawPtrTestHelper::Create(allocator_); - std::move(object_for_repeating_callback1->repeating_callback).Run(); - EXPECT_TRUE(IsQuarantineEmpty(allocator_)); - - // `RepeatingCallback` has both lvalue and rvalue versions of `Run`. - auto* object_for_repeating_callback2 = - BoundRawPtrTestHelper::Create(allocator_); - object_for_repeating_callback2->repeating_callback.Run(); - EXPECT_TRUE(IsQuarantineEmpty(allocator_)); -} - -#if PA_CONFIG(REF_COUNT_CHECK_COOKIE) -TEST_F(BackupRefPtrTest, ReinterpretCast) { - void* ptr = allocator_.root()->Alloc(16, ""); - allocator_.root()->Free(ptr); - - raw_ptr<void>* wrapped_ptr = reinterpret_cast<raw_ptr<void>*>(&ptr); - // The reference count cookie check should detect that the allocation has - // been already freed. - BASE_EXPECT_DEATH(*wrapped_ptr = nullptr, ""); -} -#endif - -namespace { - -// Install dangling raw_ptr handlers and restore them when going out of scope. -class ScopedInstallDanglingRawPtrChecks { - public: - ScopedInstallDanglingRawPtrChecks() { - enabled_feature_list_.InitWithFeaturesAndParameters( - {{features::kPartitionAllocDanglingPtr, {{"mode", "crash"}}}}, - {/* disabled_features */}); - old_detected_fn_ = partition_alloc::GetDanglingRawPtrDetectedFn(); - old_dereferenced_fn_ = partition_alloc::GetDanglingRawPtrReleasedFn(); - allocator::InstallDanglingRawPtrChecks(); - } - ~ScopedInstallDanglingRawPtrChecks() { - partition_alloc::SetDanglingRawPtrDetectedFn(old_detected_fn_); - partition_alloc::SetDanglingRawPtrReleasedFn(old_dereferenced_fn_); - } - - private: - test::ScopedFeatureList enabled_feature_list_; - partition_alloc::DanglingRawPtrDetectedFn* old_detected_fn_; - partition_alloc::DanglingRawPtrReleasedFn* old_dereferenced_fn_; -}; - -} // namespace - -TEST_F(BackupRefPtrTest, RawPtrMayDangle) { - ScopedInstallDanglingRawPtrChecks enable_dangling_raw_ptr_checks; - - void* ptr = allocator_.root()->Alloc(16, ""); - raw_ptr<void, DisableDanglingPtrDetection> dangling_ptr = ptr; - allocator_.root()->Free(ptr); // No dangling raw_ptr reported. - dangling_ptr = nullptr; // No dangling raw_ptr reported. -} - -TEST_F(BackupRefPtrTest, RawPtrNotDangling) { - ScopedInstallDanglingRawPtrChecks enable_dangling_raw_ptr_checks; - - void* ptr = allocator_.root()->Alloc(16, ""); - raw_ptr<void> dangling_ptr = ptr; -#if BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) && \ - !BUILDFLAG(ENABLE_DANGLING_RAW_PTR_PERF_EXPERIMENT) - BASE_EXPECT_DEATH( - { - allocator_.root()->Free(ptr); // Dangling raw_ptr detected. - dangling_ptr = nullptr; // Dangling raw_ptr released. - }, - AllOf(HasSubstr("Detected dangling raw_ptr"), - HasSubstr("The memory was freed at:"), - HasSubstr("The dangling raw_ptr was released at:"))); -#else - allocator_.root()->Free(ptr); - dangling_ptr = nullptr; -#endif -} - -// Check the comparator operators work, even across raw_ptr with different -// dangling policies. -TEST_F(BackupRefPtrTest, DanglingPtrComparison) { - ScopedInstallDanglingRawPtrChecks enable_dangling_raw_ptr_checks; - - void* ptr_1 = allocator_.root()->Alloc(16, ""); - void* ptr_2 = allocator_.root()->Alloc(16, ""); - - if (ptr_1 > ptr_2) { - std::swap(ptr_1, ptr_2); - } - - raw_ptr<void, DisableDanglingPtrDetection> dangling_ptr_1 = ptr_1; - raw_ptr<void, DisableDanglingPtrDetection> dangling_ptr_2 = ptr_2; - raw_ptr<void> not_dangling_ptr_1 = ptr_1; - raw_ptr<void> not_dangling_ptr_2 = ptr_2; - - EXPECT_EQ(dangling_ptr_1, not_dangling_ptr_1); - EXPECT_EQ(dangling_ptr_2, not_dangling_ptr_2); - EXPECT_NE(dangling_ptr_1, not_dangling_ptr_2); - EXPECT_NE(dangling_ptr_2, not_dangling_ptr_1); - EXPECT_LT(dangling_ptr_1, not_dangling_ptr_2); - EXPECT_GT(dangling_ptr_2, not_dangling_ptr_1); - EXPECT_LT(not_dangling_ptr_1, dangling_ptr_2); - EXPECT_GT(not_dangling_ptr_2, dangling_ptr_1); - - not_dangling_ptr_1 = nullptr; - not_dangling_ptr_2 = nullptr; - - allocator_.root()->Free(ptr_1); - allocator_.root()->Free(ptr_2); -} - -// Check the assignment operator works, even across raw_ptr with different -// dangling policies (only `not dangling` -> `dangling` direction is supported). -TEST_F(BackupRefPtrTest, DanglingPtrAssignment) { - ScopedInstallDanglingRawPtrChecks enable_dangling_raw_ptr_checks; - - void* ptr = allocator_.root()->Alloc(16, ""); - - raw_ptr<void, DisableDanglingPtrDetection> dangling_ptr; - raw_ptr<void> not_dangling_ptr; - - not_dangling_ptr = ptr; - dangling_ptr = not_dangling_ptr; - not_dangling_ptr = nullptr; - - allocator_.root()->Free(ptr); - - dangling_ptr = nullptr; -} - -// Check the copy constructor works, even across raw_ptr with different dangling -// policies (only `not dangling` -> `dangling` direction is supported). -TEST_F(BackupRefPtrTest, DanglingPtrCopyContructor) { - ScopedInstallDanglingRawPtrChecks enable_dangling_raw_ptr_checks; - - void* ptr = allocator_.root()->Alloc(16, ""); - - raw_ptr<void> not_dangling_ptr(ptr); - raw_ptr<void, DisableDanglingPtrDetection> dangling_ptr(not_dangling_ptr); - - not_dangling_ptr = nullptr; - dangling_ptr = nullptr; - - allocator_.root()->Free(ptr); -} - -TEST_F(BackupRefPtrTest, RawPtrExtractAsDangling) { - ScopedInstallDanglingRawPtrChecks enable_dangling_raw_ptr_checks; - - raw_ptr<int> ptr = - static_cast<int*>(allocator_.root()->Alloc(sizeof(int), "")); - allocator_.root()->Free( - ptr.ExtractAsDangling()); // No dangling raw_ptr reported. - EXPECT_EQ(ptr, nullptr); -} - -TEST_F(BackupRefPtrTest, RawPtrDeleteWithoutExtractAsDangling) { - ScopedInstallDanglingRawPtrChecks enable_dangling_raw_ptr_checks; - - raw_ptr<int> ptr = - static_cast<int*>(allocator_.root()->Alloc(sizeof(int), "")); -#if BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) && \ - !BUILDFLAG(ENABLE_DANGLING_RAW_PTR_PERF_EXPERIMENT) - BASE_EXPECT_DEATH( - { - allocator_.root()->Free(ptr.get()); // Dangling raw_ptr detected. - ptr = nullptr; // Dangling raw_ptr released. - }, - AllOf(HasSubstr("Detected dangling raw_ptr"), - HasSubstr("The memory was freed at:"), - HasSubstr("The dangling raw_ptr was released at:"))); -#else - allocator_.root()->Free(ptr.get()); - ptr = nullptr; -#endif // BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) && \ - // !BUILDFLAG(ENABLE_DANGLING_RAW_PTR_PERF_EXPERIMENT) -} - -TEST_F(BackupRefPtrTest, SpatialAlgoCompat) { - size_t slot_size = 512; - size_t requested_size = - allocator_.root()->AdjustSizeForExtrasSubtract(slot_size); - // Verify that we're indeed filling up the slot. - ASSERT_EQ( - requested_size, - allocator_.root()->AllocationCapacityFromRequestedSize(requested_size)); - size_t requested_elements = requested_size / sizeof(int); - - int* ptr = - reinterpret_cast<int*>(allocator_.root()->Alloc(requested_size, "")); - int* ptr_end = ptr + requested_elements; - - CountingRawPtr<int> protected_ptr = ptr; - CountingRawPtr<int> protected_ptr_end = protected_ptr + requested_elements; - -#if BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR) - EXPECT_DEATH_IF_SUPPORTED(*protected_ptr_end = 1, ""); -#endif - - RawPtrCountingImpl::ClearCounters(); - - int gen_val = 1; - std::generate(protected_ptr, protected_ptr_end, [&gen_val]() { - gen_val ^= gen_val + 1; - return gen_val; - }); - - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = requested_elements, - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = (requested_elements + 1) * 2, - }), - CountersMatch()); - - RawPtrCountingImpl::ClearCounters(); - - for (CountingRawPtr<int> protected_ptr_i = protected_ptr; - protected_ptr_i < protected_ptr_end; protected_ptr_i++) { - *protected_ptr_i ^= *protected_ptr_i + 1; - } - - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = requested_elements * 2, - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = (requested_elements + 1) * 2, - }), - CountersMatch()); - - RawPtrCountingImpl::ClearCounters(); - - for (CountingRawPtr<int> protected_ptr_i = protected_ptr; - protected_ptr_i < ptr_end; protected_ptr_i++) { - *protected_ptr_i ^= *protected_ptr_i + 1; - } - - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = requested_elements * 2, - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = requested_elements + 1, - }), - CountersMatch()); - - RawPtrCountingImpl::ClearCounters(); - - for (int* ptr_i = ptr; ptr_i < protected_ptr_end; ptr_i++) { - *ptr_i ^= *ptr_i + 1; - } - - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 0, - .get_for_comparison_cnt = requested_elements + 1, - }), - CountersMatch()); - - RawPtrCountingImpl::ClearCounters(); - - size_t iter_cnt = 0; - for (int *ptr_i = protected_ptr, *ptr_i_end = protected_ptr_end; - ptr_i < ptr_i_end; ptr_i++) { - *ptr_i ^= *ptr_i + 1; - iter_cnt++; - } - EXPECT_EQ(iter_cnt, requested_elements); - - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 2, - .get_for_comparison_cnt = 0, - }), - CountersMatch()); - - allocator_.root()->Free(ptr); -} - -#if BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR) -TEST_F(BackupRefPtrTest, Duplicate) { - size_t requested_size = allocator_.root()->AdjustSizeForExtrasSubtract(512); - char* ptr = static_cast<char*>(allocator_.root()->Alloc(requested_size, "")); - raw_ptr<char> protected_ptr1 = ptr; - protected_ptr1 += requested_size; // Pointer should now be poisoned. - - // Duplicating a poisoned pointer should be allowed. - raw_ptr<char> protected_ptr2 = protected_ptr1; - - // The poison bit should be propagated to the duplicate such that the OOB - // access is disallowed: - EXPECT_DEATH_IF_SUPPORTED(*protected_ptr2 = ' ', ""); - - // Assignment from a poisoned pointer should be allowed. - raw_ptr<char> protected_ptr3; - protected_ptr3 = protected_ptr1; - - // The poison bit should be propagated via the assignment such that the OOB - // access is disallowed: - EXPECT_DEATH_IF_SUPPORTED(*protected_ptr3 = ' ', ""); - - allocator_.root()->Free(ptr); -} -#endif // BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR) - -#if BUILDFLAG(PA_EXPENSIVE_DCHECKS_ARE_ON) -TEST_F(BackupRefPtrTest, WriteAfterFree) { - constexpr uint64_t kPayload = 0x1234567890ABCDEF; - - raw_ptr<uint64_t> ptr = - static_cast<uint64_t*>(allocator_.root()->Alloc(sizeof(uint64_t), "")); - - // Now |ptr| should be quarantined. - allocator_.root()->Free(ptr); - - EXPECT_DEATH_IF_SUPPORTED( - { - // Write something different from |kQuarantinedByte|. - *ptr = kPayload; - // Write-after-Free should lead to crash - // on |PartitionAllocFreeForRefCounting|. - ptr = nullptr; - }, - ""); -} -#endif // BUILDFLAG(PA_EXPENSIVE_DCHECKS_ARE_ON) - -namespace { -constexpr uint8_t kCustomQuarantineByte = 0xff; -static_assert(kCustomQuarantineByte != - partition_alloc::internal::kQuarantinedByte); - -void CustomQuarantineHook(void* address, size_t size) { - partition_alloc::internal::SecureMemset(address, kCustomQuarantineByte, size); -} -} // namespace - -TEST_F(BackupRefPtrTest, QuarantineHook) { - partition_alloc::PartitionAllocHooks::SetQuarantineOverrideHook( - CustomQuarantineHook); - uint8_t* native_ptr = - static_cast<uint8_t*>(allocator_.root()->Alloc(sizeof(uint8_t), "")); - *native_ptr = 0; - { - raw_ptr<uint8_t> smart_ptr = native_ptr; - - allocator_.root()->Free(smart_ptr); - // Access the allocation through the native pointer to avoid triggering - // dereference checks in debug builds. - EXPECT_EQ(*partition_alloc::internal::TagPtr(native_ptr), - kCustomQuarantineByte); - - // Leaving |smart_ptr| filled with |kCustomQuarantineByte| can - // cause a crash because we have a DCHECK that expects it to be filled with - // |kQuarantineByte|. We need to ensure it is unquarantined before - // unregistering the hook. - } // <- unquarantined here - - partition_alloc::PartitionAllocHooks::SetQuarantineOverrideHook(nullptr); -} - -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) && - // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - -#if BUILDFLAG(USE_HOOKABLE_RAW_PTR) - -namespace { -#define FOR_EACH_RAW_PTR_OPERATION(F) \ - F(wrap_ptr) \ - F(release_wrapped_ptr) \ - F(safely_unwrap_for_dereference) \ - F(safely_unwrap_for_extraction) \ - F(unsafely_unwrap_for_comparison) \ - F(advance) \ - F(duplicate) - -// Can't use gMock to count the number of invocations because -// gMock itself triggers raw_ptr<T> operations. -struct CountingHooks { - void ResetCounts() { -#define F(name) name##_count = 0; - FOR_EACH_RAW_PTR_OPERATION(F) -#undef F - } - - static CountingHooks* Get() { - static thread_local CountingHooks instance; - return &instance; - } - -// The adapter method is templated to accept any number of arguments. -#define F(name) \ - template <typename... T> \ - static void name##_adapter(T...) { \ - Get()->name##_count++; \ - } \ - size_t name##_count = 0; - FOR_EACH_RAW_PTR_OPERATION(F) -#undef F -}; - -constexpr RawPtrHooks raw_ptr_hooks{ -#define F(name) .name = CountingHooks::name##_adapter, - FOR_EACH_RAW_PTR_OPERATION(F) -#undef F -}; -} // namespace - -class HookableRawPtrImplTest : public testing::Test { - protected: - void SetUp() override { InstallRawPtrHooks(&raw_ptr_hooks); } - void TearDown() override { ResetRawPtrHooks(); } -}; - -TEST_F(HookableRawPtrImplTest, WrapPtr) { - // Can't call `ResetCounts` in `SetUp` because gTest triggers - // raw_ptr<T> operations between `SetUp` and the test body. - CountingHooks::Get()->ResetCounts(); - { - int* ptr = new int; - [[maybe_unused]] raw_ptr<int> interesting_ptr = ptr; - delete ptr; - } - EXPECT_EQ(CountingHooks::Get()->wrap_ptr_count, 1u); -} - -TEST_F(HookableRawPtrImplTest, ReleaseWrappedPtr) { - CountingHooks::Get()->ResetCounts(); - { - int* ptr = new int; - [[maybe_unused]] raw_ptr<int> interesting_ptr = ptr; - delete ptr; - } - EXPECT_EQ(CountingHooks::Get()->release_wrapped_ptr_count, 1u); -} - -TEST_F(HookableRawPtrImplTest, SafelyUnwrapForDereference) { - CountingHooks::Get()->ResetCounts(); - { - int* ptr = new int; - raw_ptr<int> interesting_ptr = ptr; - *interesting_ptr = 1; - delete ptr; - } - EXPECT_EQ(CountingHooks::Get()->safely_unwrap_for_dereference_count, 1u); -} - -TEST_F(HookableRawPtrImplTest, SafelyUnwrapForExtraction) { - CountingHooks::Get()->ResetCounts(); - { - int* ptr = new int; - raw_ptr<int> interesting_ptr = ptr; - ptr = interesting_ptr; - delete ptr; - } - EXPECT_EQ(CountingHooks::Get()->safely_unwrap_for_extraction_count, 1u); -} - -TEST_F(HookableRawPtrImplTest, UnsafelyUnwrapForComparison) { - CountingHooks::Get()->ResetCounts(); - { - int* ptr = new int; - raw_ptr<int> interesting_ptr = ptr; - EXPECT_EQ(interesting_ptr, ptr); - delete ptr; - } - EXPECT_EQ(CountingHooks::Get()->unsafely_unwrap_for_comparison_count, 1u); -} - -TEST_F(HookableRawPtrImplTest, Advance) { - CountingHooks::Get()->ResetCounts(); - { - int* ptr = new int[10]; - raw_ptr<int, AllowPtrArithmetic> interesting_ptr = ptr; - interesting_ptr += 1; - delete[] ptr; - } - EXPECT_EQ(CountingHooks::Get()->advance_count, 1u); -} - -TEST_F(HookableRawPtrImplTest, Duplicate) { - CountingHooks::Get()->ResetCounts(); - { - int* ptr = new int; - raw_ptr<int> interesting_ptr = ptr; - raw_ptr<int> interesting_ptr2 = interesting_ptr; - delete ptr; - } - EXPECT_EQ(CountingHooks::Get()->duplicate_count, 1u); -} - -#endif // BUILDFLAG(USE_HOOKABLE_RAW_PTR) - -TEST(DanglingPtrTest, DetectAndReset) { - auto instrumentation = test::DanglingPtrInstrumentation::Create(); - if (!instrumentation.has_value()) { - GTEST_SKIP() << instrumentation.error(); - } - - auto owned_ptr = std::make_unique<int>(42); - raw_ptr<int> dangling_ptr = owned_ptr.get(); - EXPECT_EQ(instrumentation->dangling_ptr_detected(), 0u); - EXPECT_EQ(instrumentation->dangling_ptr_released(), 0u); - owned_ptr.reset(); - EXPECT_EQ(instrumentation->dangling_ptr_detected(), 1u); - EXPECT_EQ(instrumentation->dangling_ptr_released(), 0u); - dangling_ptr = nullptr; - EXPECT_EQ(instrumentation->dangling_ptr_detected(), 1u); - EXPECT_EQ(instrumentation->dangling_ptr_released(), 1u); -} - -TEST(DanglingPtrTest, DetectAndDestructor) { - auto instrumentation = test::DanglingPtrInstrumentation::Create(); - if (!instrumentation.has_value()) { - GTEST_SKIP() << instrumentation.error(); - } - - auto owned_ptr = std::make_unique<int>(42); - { - [[maybe_unused]] raw_ptr<int> dangling_ptr = owned_ptr.get(); - EXPECT_EQ(instrumentation->dangling_ptr_detected(), 0u); - EXPECT_EQ(instrumentation->dangling_ptr_released(), 0u); - owned_ptr.reset(); - EXPECT_EQ(instrumentation->dangling_ptr_detected(), 1u); - EXPECT_EQ(instrumentation->dangling_ptr_released(), 0u); - } - EXPECT_EQ(instrumentation->dangling_ptr_detected(), 1u); - EXPECT_EQ(instrumentation->dangling_ptr_released(), 1u); -} - -TEST(DanglingPtrTest, DetectResetAndDestructor) { - auto instrumentation = test::DanglingPtrInstrumentation::Create(); - if (!instrumentation.has_value()) { - GTEST_SKIP() << instrumentation.error(); - } - - auto owned_ptr = std::make_unique<int>(42); - { - raw_ptr<int> dangling_ptr = owned_ptr.get(); - EXPECT_EQ(instrumentation->dangling_ptr_detected(), 0u); - EXPECT_EQ(instrumentation->dangling_ptr_released(), 0u); - owned_ptr.reset(); - EXPECT_EQ(instrumentation->dangling_ptr_detected(), 1u); - EXPECT_EQ(instrumentation->dangling_ptr_released(), 0u); - dangling_ptr = nullptr; - EXPECT_EQ(instrumentation->dangling_ptr_detected(), 1u); - EXPECT_EQ(instrumentation->dangling_ptr_released(), 1u); - } - EXPECT_EQ(instrumentation->dangling_ptr_detected(), 1u); - EXPECT_EQ(instrumentation->dangling_ptr_released(), 1u); -} - -} // namespace internal -} // namespace base
diff --git a/base/allocator/partition_allocator/pointers/raw_ptr_unittest.nc b/base/allocator/partition_allocator/pointers/raw_ptr_unittest.nc deleted file mode 100644 index 22aede1..0000000 --- a/base/allocator/partition_allocator/pointers/raw_ptr_unittest.nc +++ /dev/null
@@ -1,291 +0,0 @@ -// 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. - -// This is a "No Compile Test" suite. -// http://dev.chromium.org/developers/testing/no-compile-tests - -#include <memory> -#include <tuple> // for std::ignore -#include <type_traits> // for std::remove_pointer_t - -#include "base/functional/bind.h" -#include "base/functional/callback.h" -#include "base/allocator/partition_allocator/pointers/raw_ptr.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" - -namespace { - -struct Producer {}; -struct DerivedProducer : Producer {}; -struct OtherDerivedProducer : Producer {}; -struct Unrelated {}; -struct DerivedUnrelated : Unrelated {}; -struct PmfTest { - public: - int Func(char, double) const { return 11; } -}; - -#if defined(NCTEST_INVALID_RAW_PTR_TRAIT) // [r"Unknown raw_ptr trait\(s\)"] - -void WontCompile() { - constexpr auto InvalidRawPtrTrait = - ~base::RawPtrTraits::kEmpty; - raw_ptr<int, InvalidRawPtrTrait> p; -} - -#elif defined(NCTEST_INVALID_RAW_PTR_TRAIT_OF_MANY) // [r"Unknown raw_ptr trait\(s\)"] - -void WontCompile() { - constexpr auto InvalidRawPtrTrait = ~base::RawPtrTraits::kEmpty; - raw_ptr<int, DisableDanglingPtrDetection | InvalidRawPtrTrait> - p; -} - -#elif defined(NCTEST_AUTO_DOWNCAST) // [r"no viable conversion from 'raw_ptr<Producer>' to 'raw_ptr<DerivedProducer>'"] - -void WontCompile() { - Producer f; - raw_ptr<Producer> ptr = &f; - raw_ptr<DerivedProducer> derived_ptr = ptr; -} - -#elif defined(NCTEST_STATIC_DOWNCAST) // [r"no matching conversion for static_cast from 'raw_ptr<Producer>' to 'raw_ptr<DerivedProducer>'"] - -void WontCompile() { - Producer f; - raw_ptr<Producer> ptr = &f; - raw_ptr<DerivedProducer> derived_ptr = - static_cast<raw_ptr<DerivedProducer>>(ptr); -} - -#elif defined(NCTEST_AUTO_REF_DOWNCAST) // [r"non-const lvalue reference to type 'raw_ptr<DerivedProducer>' cannot bind to a value of unrelated type 'raw_ptr<Producer>'"] - -void WontCompile() { - Producer f; - raw_ptr<Producer> ptr = &f; - raw_ptr<DerivedProducer>& derived_ptr = ptr; -} - -#elif defined(NCTEST_STATIC_REF_DOWNCAST) // [r"non-const lvalue reference to type 'raw_ptr<DerivedProducer>' cannot bind to a value of unrelated type 'raw_ptr<Producer>'"] - -void WontCompile() { - Producer f; - raw_ptr<Producer> ptr = &f; - raw_ptr<DerivedProducer>& derived_ptr = - static_cast<raw_ptr<DerivedProducer>&>(ptr); -} - -#elif defined(NCTEST_AUTO_DOWNCAST_FROM_RAW) // [r"no viable conversion from 'Producer \*' to 'raw_ptr<DerivedProducer>'"] - -void WontCompile() { - Producer f; - raw_ptr<DerivedProducer> ptr = &f; -} - -#elif defined(NCTEST_UNRELATED_FROM_RAW) // [r"no viable conversion from 'DerivedProducer \*' to 'raw_ptr<Unrelated>'"] - -void WontCompile() { - DerivedProducer f; - raw_ptr<Unrelated> ptr = &f; -} - -#elif defined(NCTEST_UNRELATED_STATIC_FROM_WRAPPED) // [r"static_cast from '\(anonymous namespace\)::DerivedProducer \*' to '\(anonymous namespace\)::Unrelated \*', which are not related by inheritance, is not allowed"] - -void WontCompile() { - DerivedProducer f; - raw_ptr<DerivedProducer> ptr = &f; - std::ignore = static_cast<Unrelated*>(ptr); -} - -#elif defined(NCTEST_VOID_DEREFERENCE) // [r"indirection requires pointer operand \('raw_ptr<const void>' invalid\)"] - -void WontCompile() { - const char foo[] = "42"; - raw_ptr<const void> ptr = foo; - std::ignore = *ptr; -} - -#elif defined(NCTEST_FUNCTION_POINTER) // [r"raw_ptr<T> doesn't work with this kind of pointee type T"] - -void WontCompile() { - raw_ptr<void(int)> raw_ptr_var; - std::ignore = raw_ptr_var.get(); -} - -#elif defined(NCTEST_POINTER_TO_MEMBER) // [r"overload resolution selected deleted operator '->\*'"] - -void WontCompile() { - PmfTest object; - int (PmfTest::*pmf_func)(char, double) const = &PmfTest::Func; - - raw_ptr<PmfTest> object_ptr = &object; - std::ignore = object_ptr->*pmf_func; -} - -#elif defined(NCTEST_DANGLING_GSL) // [r"object backing the pointer will be destroyed at the end of the full-expression"] - -void WontCompile() { - [[maybe_unused]] raw_ptr<int> ptr = std::make_unique<int>(2).get(); -} - -#elif defined(NCTEST_BINDING_RAW_PTR_PARAMETER) // [r"base::Bind\(\) target functor has a parameter of type raw_ptr<T>."] - -void WontCompile() { - raw_ptr<int> ptr = new int(3); - - // Make sure that we are not allowed to bind a function with a raw_ptr<T> - // parameter type. - auto callback = base::BindOnce( - [](raw_ptr<int> ptr) { - }, - ptr); -} - -#elif defined(NCTEST_BINDING_RAW_PTR_DISALLOW_PLUS_EQUALS_STRUCT) // [r"no viable overloaded '\+='"] - -void WontCompile() { - raw_ptr<int> ptr = new int(3); - struct {} s; - ptr += s; -} - -#elif defined(NCTEST_BINDING_RAW_PTR_DISALLOW_MINUS_EQUALS_STRUCT) // [r"no viable overloaded '-='"] - -void WontCompile() { - raw_ptr<int> ptr = new int(3); - struct {} s; - ptr -= s; -} - -#elif defined(NCTEST_BINDING_RAW_PTR_DISALLOW_PLUS_STRUCT) // [r"no viable overloaded '\+='"] - -void WontCompile() { - raw_ptr<int> ptr = new int(3); - struct {} s; - // Note, operator + exists, but it calls += which doesn't. - [[maybe_unused]] raw_ptr<int> ptr2 = ptr + s; -} - -#elif defined(NCTEST_BINDING_RAW_PTR_DISALLOW_MINUS_STRUCT) // [r"no viable overloaded '-='"] - -void WontCompile() { - raw_ptr<int> ptr = new int(3); - struct {} s; - // Note, operator - exists, but it calls -= which doesn't. - [[maybe_unused]] raw_ptr<int> ptr2 = ptr - s; -} - -#elif defined(NCTEST_BINDING_RAW_PTR_DISALLOW_PLUS_EQUALS_UINT64) // [r"no viable overloaded '\+='"] - -void WontCompile() { -#if !BUILDFLAG(HAS_64_BIT_POINTERS) - raw_ptr<int> ptr = new int(3); - ptr += uint64_t{2}; -#else - // Fake error on 64-bit to match the expectation. - static_assert(false, "no viable overloaded '+='"); -#endif // !BUILDFLAG(HAS_64_BIT_POINTERS) -} - -#elif defined(NCTEST_BINDING_RAW_PTR_DISALLOW_MINUS_EQUALS_UINT64) // [r"no viable overloaded '-='"] - -void WontCompile() { -#if !BUILDFLAG(HAS_64_BIT_POINTERS) - raw_ptr<int> ptr = new int(3); - ptr -= uint64_t{2}; -#else - // Fake error on 64-bit to match the expectation. - static_assert(false, "no viable overloaded '-='"); -#endif // !BUILDFLAG(HAS_64_BIT_POINTERS) -} - -#elif defined(NCTEST_BINDING_RAW_PTR_DISALLOW_PLUS_UINT64) // [r"no viable overloaded '\+='"] - -void WontCompile() { -#if !BUILDFLAG(HAS_64_BIT_POINTERS) - raw_ptr<int> ptr = new int(3); - // Note, operator + exists, but it calls += which doesn't. - [[maybe_unused]] raw_ptr<int> ptr2 = ptr + uint64_t{2}; -#else - // Fake error on 64-bit to match the expectation. - static_assert(false, "no viable overloaded '+='"); -#endif // !BUILDFLAG(HAS_64_BIT_POINTERS) -} - -#elif defined(NCTEST_BINDING_RAW_PTR_DISALLOW_MINUS_UINT64) // [r"no viable overloaded '-='"] - -void WontCompile() { -#if !BUILDFLAG(HAS_64_BIT_POINTERS) - raw_ptr<int> ptr = new int(3); - // Note, operator - exists, but it calls -= which doesn't. - [[maybe_unused]] raw_ptr<int> ptr2 = ptr - uint64_t{2}; -#else - // Fake error on 64-bit to match the expectation. - static_assert(false, "no viable overloaded '-='"); -#endif // !BUILDFLAG(HAS_64_BIT_POINTERS) -} - -#elif defined(NCTEST_CROSS_KIND_CONVERSION_FROM_MAY_DANGLE) // [r"static assertion failed due to requirement '\(base::RawPtrTraits\)0U == \(\(base::RawPtrTraits\)1U | RawPtrTraits::kMayDangle\)'"] - -void WontCompile() { - raw_ptr<int, base::RawPtrTraits::kMayDangle> ptr = new int(3); - [[maybe_unused]] raw_ptr<int> ptr2(ptr); -} - -#elif defined(NCTEST_CROSS_KIND_CONVERSION_FROM_DUMMY) // [r"static assertion failed due to requirement '\(base::RawPtrTraits\)0U == \(\(base::RawPtrTraits\)1U | RawPtrTraits::kMayDangle\)'"] - -void WontCompile() { - raw_ptr<int, base::RawPtrTraits::kDummyForTest> ptr = new int(3); - [[maybe_unused]] raw_ptr<int, base::RawPtrTraits::kMayDangle> ptr2(ptr); -} - -#elif defined(NCTEST_CROSS_KIND_MOVE_CONVERSION_FROM_MAY_DANGLE) // [r"static assertion failed due to requirement '\(base::RawPtrTraits\)0U == \(\(base::RawPtrTraits\)1U | RawPtrTraits::kMayDangle\)'"] - -void WontCompile() { - raw_ptr<int, base::RawPtrTraits::kMayDangle> ptr = new int(3); - [[maybe_unused]] raw_ptr<int> ptr2(std::move(ptr)); -} - -#elif defined(NCTEST_CROSS_KIND_MOVE_CONVERSION_FROM_DUMMY) // [r"static assertion failed due to requirement '\(base::RawPtrTraits\)0U == \(\(base::RawPtrTraits\)1U | RawPtrTraits::kMayDangle\)'"] - -void WontCompile() { - raw_ptr<int, base::RawPtrTraits::kDummyForTest> ptr = new int(3); - [[maybe_unused]] raw_ptr<int, base::RawPtrTraits::kMayDangle> ptr2(std::move(ptr)); -} - -#elif defined(NCTEST_CROSS_KIND_ASSIGNMENT_FROM_MAY_DANGLE) // [r"static assertion failed due to requirement '\(base::RawPtrTraits\)1U == \(\(base::RawPtrTraits\)16U | RawPtrTraits::kMayDangle\)'"] - -void WontCompile() { - raw_ptr<int, base::RawPtrTraits::kMayDangle> ptr = new int(3); - raw_ptr<int> ptr2; - ptr2 = ptr; -} - -#elif defined(NCTEST_CROSS_KIND_ASSIGNMENT_FROM_DUMMY) // [r"static assertion failed due to requirement '\(base::RawPtrTraits\)1U == \(\(base::RawPtrTraits\)16U | RawPtrTraits::kMayDangle\)'"] - -void WontCompile() { - raw_ptr<int, base::RawPtrTraits::kDummyForTest> ptr = new int(3); - raw_ptr<int, base::RawPtrTraits::kMayDangle> ptr2; - ptr2 = ptr; -} - -#elif defined(NCTEST_CROSS_KIND_MOVE_ASSIGNMENT_FROM_MAY_DANGLE) // [r"static assertion failed due to requirement '\(base::RawPtrTraits\)1U == \(\(base::RawPtrTraits\)16U | RawPtrTraits::kMayDangle\)'"] - -void WontCompile() { - raw_ptr<int, base::RawPtrTraits::kMayDangle> ptr = new int(3); - raw_ptr<int> ptr2; - ptr2 = std::move(ptr); -} - -#elif defined(NCTEST_CROSS_KIND_MOVE_ASSIGNMENT_FROM_DUMMY) // [r"static assertion failed due to requirement '\(base::RawPtrTraits\)1U == \(\(base::RawPtrTraits\)16U | RawPtrTraits::kMayDangle\)'"] - -void WontCompile() { - raw_ptr<int, base::RawPtrTraits::kDummyForTest> ptr = new int(3); - raw_ptr<int, base::RawPtrTraits::kMayDangle> ptr2; - ptr2 = std::move(ptr); -} - -#endif - -} // namespace
diff --git a/base/allocator/partition_allocator/pointers/raw_ref.h b/base/allocator/partition_allocator/pointers/raw_ref.h deleted file mode 100644 index 446da7c..0000000 --- a/base/allocator/partition_allocator/pointers/raw_ref.h +++ /dev/null
@@ -1,441 +0,0 @@ -// 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. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_POINTERS_RAW_REF_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_POINTERS_RAW_REF_H_ - -#include <memory> -#include <type_traits> -#include <utility> - -#include "base/allocator/partition_allocator/partition_alloc_base/augmentations/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_config.h" -#include "base/allocator/partition_allocator/pointers/raw_ptr.h" - -namespace base { - -template <class T, RawPtrTraits Traits> -class raw_ref; - -namespace internal { - -template <class T> -struct is_raw_ref : std::false_type {}; - -template <class T, RawPtrTraits Traits> -struct is_raw_ref<::base::raw_ref<T, Traits>> : std::true_type {}; - -template <class T> -constexpr inline bool is_raw_ref_v = is_raw_ref<T>::value; - -} // namespace internal - -// A smart pointer for a pointer which can not be null, and which provides -// Use-after-Free protection in the same ways as raw_ptr. This class acts like a -// combination of std::reference_wrapper and raw_ptr. -// -// See raw_ptr and //base/memory/raw_ptr.md for more details on the -// Use-after-Free protection. -// -// # Use after move -// -// The raw_ref type will abort if used after being moved. -// -// # Constness -// -// Use a `const raw_ref<T>` when the smart pointer should not be able to rebind -// to a new reference. Use a `const raw_ref<const T>` do the same for a const -// reference, which is like `const T&`. -// -// Unlike a native `T&` reference, a mutable `raw_ref<T>` can be changed -// independent of the underlying `T`, similar to `std::reference_wrapper`. That -// means the reference inside it can be moved and reassigned. -template <class T, RawPtrTraits Traits = RawPtrTraits::kEmpty> -class PA_TRIVIAL_ABI PA_GSL_POINTER raw_ref { - // operator* is used with the expectation of GetForExtraction semantics: - // - // raw_ref<Foo> foo_raw_ref = something; - // Foo& foo_ref = *foo_raw_ref; - // - // The implementation of operator* provides GetForDereference semantics, and - // this results in spurious crashes in BRP-ASan builds, so we need to disable - // hooks that provide BRP-ASan instrumentation for raw_ref. - using Inner = raw_ptr<T, Traits | RawPtrTraits::kDisableHooks>; - - // Some underlying implementations do not clear on move, which produces an - // inconsistent behaviour. We want consistent behaviour such that using a - // raw_ref after move is caught and aborts, so do it when the underlying - // implementation doesn't. Failure to clear would be indicated by the related - // death tests not CHECKing appropriately. - static constexpr bool kNeedClearAfterMove = !Inner::kZeroOnMove; - - public: - using Impl = typename Inner::Impl; - - // Construct a raw_ref from a pointer, which must not be null. - // - // This function is safe to use with any pointer, as it will CHECK and - // terminate the process if the pointer is null. Avoid dereferencing a pointer - // to avoid this CHECK as you may be dereferencing null. - PA_ALWAYS_INLINE constexpr static raw_ref from_ptr(T* ptr) noexcept { - PA_RAW_PTR_CHECK(ptr); - return raw_ref(*ptr); - } - - // Construct a raw_ref from a reference. - PA_ALWAYS_INLINE constexpr explicit raw_ref(T& p) noexcept - : inner_(std::addressof(p)) {} - - // Assign a new reference to the raw_ref, replacing the existing reference. - PA_ALWAYS_INLINE constexpr raw_ref& operator=(T& p) noexcept { - inner_.operator=(&p); - return *this; - } - - // Disallow holding references to temporaries. - raw_ref(const T&& p) = delete; - raw_ref& operator=(const T&& p) = delete; - - PA_ALWAYS_INLINE constexpr raw_ref(const raw_ref& p) noexcept - : inner_(p.inner_) { - PA_RAW_PTR_CHECK(inner_); // Catch use-after-move. - } - - PA_ALWAYS_INLINE constexpr raw_ref(raw_ref&& p) noexcept - : inner_(std::move(p.inner_)) { - PA_RAW_PTR_CHECK(inner_); // Catch use-after-move. - if constexpr (kNeedClearAfterMove) { - p.inner_ = nullptr; - } - } - - PA_ALWAYS_INLINE constexpr raw_ref& operator=(const raw_ref& p) noexcept { - PA_RAW_PTR_CHECK(p.inner_); // Catch use-after-move. - inner_.operator=(p.inner_); - return *this; - } - - PA_ALWAYS_INLINE constexpr raw_ref& operator=(raw_ref&& p) noexcept { - PA_RAW_PTR_CHECK(p.inner_); // Catch use-after-move. - inner_.operator=(std::move(p.inner_)); - if constexpr (kNeedClearAfterMove) { - p.inner_ = nullptr; - } - return *this; - } - - // Deliberately implicit in order to support implicit upcast. - // Delegate cross-kind conversion to the inner raw_ptr, which decides when to - // allow it. - template <class U, - RawPtrTraits PassedTraits, - class = std::enable_if_t<std::is_convertible_v<U&, T&>>> - // NOLINTNEXTLINE(google-explicit-constructor) - PA_ALWAYS_INLINE constexpr raw_ref(const raw_ref<U, PassedTraits>& p) noexcept - : inner_(p.inner_) { - PA_RAW_PTR_CHECK(inner_); // Catch use-after-move. - } - // Deliberately implicit in order to support implicit upcast. - // Delegate cross-kind conversion to the inner raw_ptr, which decides when to - // allow it. - template <class U, - RawPtrTraits PassedTraits, - class = std::enable_if_t<std::is_convertible_v<U&, T&>>> - // NOLINTNEXTLINE(google-explicit-constructor) - PA_ALWAYS_INLINE constexpr raw_ref(raw_ref<U, PassedTraits>&& p) noexcept - : inner_(std::move(p.inner_)) { - PA_RAW_PTR_CHECK(inner_); // Catch use-after-move. - if constexpr (kNeedClearAfterMove) { - p.inner_ = nullptr; - } - } - - // Upcast assignment - // Delegate cross-kind conversion to the inner raw_ptr, which decides when to - // allow it. - template <class U, - RawPtrTraits PassedTraits, - class = std::enable_if_t<std::is_convertible_v<U&, T&>>> - PA_ALWAYS_INLINE constexpr raw_ref& operator=( - const raw_ref<U, PassedTraits>& p) noexcept { - PA_RAW_PTR_CHECK(p.inner_); // Catch use-after-move. - inner_.operator=(p.inner_); - return *this; - } - // Delegate cross-kind conversion to the inner raw_ptr, which decides when to - // allow it. - template <class U, - RawPtrTraits PassedTraits, - class = std::enable_if_t<std::is_convertible_v<U&, T&>>> - PA_ALWAYS_INLINE constexpr raw_ref& operator=( - raw_ref<U, PassedTraits>&& p) noexcept { - PA_RAW_PTR_CHECK(p.inner_); // Catch use-after-move. - inner_.operator=(std::move(p.inner_)); - if constexpr (kNeedClearAfterMove) { - p.inner_ = nullptr; - } - return *this; - } - - PA_ALWAYS_INLINE constexpr T& operator*() const { - PA_RAW_PTR_CHECK(inner_); // Catch use-after-move. - return inner_.operator*(); - } - - // This is an equivalent to operator*() that provides GetForExtraction rather - // rather than GetForDereference semantics (see raw_ptr.h). This should be - // used in place of operator*() when the memory referred to by the reference - // is not immediately going to be accessed. - PA_ALWAYS_INLINE constexpr T& get() const { - PA_RAW_PTR_CHECK(inner_); // Catch use-after-move. - return *inner_.get(); - } - - PA_ALWAYS_INLINE constexpr T* operator->() const - PA_ATTRIBUTE_RETURNS_NONNULL { - PA_RAW_PTR_CHECK(inner_); // Catch use-after-move. - return inner_.operator->(); - } - - // This is used to verify callbacks are not invoked with dangling references. - // If the `raw_ref` references a deleted object, it will trigger an error. - // Depending on the PartitionAllocUnretainedDanglingPtr feature, this is - // either a DumpWithoutCrashing, a crash, or ignored. - PA_ALWAYS_INLINE void ReportIfDangling() const noexcept { - inner_.ReportIfDangling(); - } - - PA_ALWAYS_INLINE friend constexpr void swap(raw_ref& lhs, - raw_ref& rhs) noexcept { - PA_RAW_PTR_CHECK(lhs.inner_); // Catch use-after-move. - PA_RAW_PTR_CHECK(rhs.inner_); // Catch use-after-move. - swap(lhs.inner_, rhs.inner_); - } - - template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2> - friend bool operator==(const raw_ref<U, Traits1>& lhs, - const raw_ref<V, Traits2>& rhs); - template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2> - friend bool operator!=(const raw_ref<U, Traits1>& lhs, - const raw_ref<V, Traits2>& rhs); - template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2> - friend bool operator<(const raw_ref<U, Traits1>& lhs, - const raw_ref<V, Traits2>& rhs); - template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2> - friend bool operator>(const raw_ref<U, Traits1>& lhs, - const raw_ref<V, Traits2>& rhs); - template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2> - friend bool operator<=(const raw_ref<U, Traits1>& lhs, - const raw_ref<V, Traits2>& rhs); - template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2> - friend bool operator>=(const raw_ref<U, Traits1>& lhs, - const raw_ref<V, Traits2>& rhs); - - template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>> - PA_ALWAYS_INLINE friend bool operator==(const raw_ref& lhs, const U& rhs) { - PA_RAW_PTR_CHECK(lhs.inner_); // Catch use-after-move. - return lhs.inner_ == &rhs; - } - template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>> - PA_ALWAYS_INLINE friend bool operator!=(const raw_ref& lhs, const U& rhs) { - PA_RAW_PTR_CHECK(lhs.inner_); // Catch use-after-move. - return lhs.inner_ != &rhs; - } - template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>> - PA_ALWAYS_INLINE friend bool operator<(const raw_ref& lhs, const U& rhs) { - PA_RAW_PTR_CHECK(lhs.inner_); // Catch use-after-move. - return lhs.inner_ < &rhs; - } - template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>> - PA_ALWAYS_INLINE friend bool operator>(const raw_ref& lhs, const U& rhs) { - PA_RAW_PTR_CHECK(lhs.inner_); // Catch use-after-move. - return lhs.inner_ > &rhs; - } - template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>> - PA_ALWAYS_INLINE friend bool operator<=(const raw_ref& lhs, const U& rhs) { - PA_RAW_PTR_CHECK(lhs.inner_); // Catch use-after-move. - return lhs.inner_ <= &rhs; - } - template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>> - PA_ALWAYS_INLINE friend bool operator>=(const raw_ref& lhs, const U& rhs) { - PA_RAW_PTR_CHECK(lhs.inner_); // Catch use-after-move. - return lhs.inner_ >= &rhs; - } - - template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>> - PA_ALWAYS_INLINE friend bool operator==(const U& lhs, const raw_ref& rhs) { - PA_RAW_PTR_CHECK(rhs.inner_); // Catch use-after-move. - return &lhs == rhs.inner_; - } - template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>> - PA_ALWAYS_INLINE friend bool operator!=(const U& lhs, const raw_ref& rhs) { - PA_RAW_PTR_CHECK(rhs.inner_); // Catch use-after-move. - return &lhs != rhs.inner_; - } - template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>> - PA_ALWAYS_INLINE friend bool operator<(const U& lhs, const raw_ref& rhs) { - PA_RAW_PTR_CHECK(rhs.inner_); // Catch use-after-move. - return &lhs < rhs.inner_; - } - template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>> - PA_ALWAYS_INLINE friend bool operator>(const U& lhs, const raw_ref& rhs) { - PA_RAW_PTR_CHECK(rhs.inner_); // Catch use-after-move. - return &lhs > rhs.inner_; - } - template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>> - PA_ALWAYS_INLINE friend bool operator<=(const U& lhs, const raw_ref& rhs) { - PA_RAW_PTR_CHECK(rhs.inner_); // Catch use-after-move. - return &lhs <= rhs.inner_; - } - template <class U, class = std::enable_if_t<!internal::is_raw_ref_v<U>, void>> - PA_ALWAYS_INLINE friend bool operator>=(const U& lhs, const raw_ref& rhs) { - PA_RAW_PTR_CHECK(rhs.inner_); // Catch use-after-move. - return &lhs >= rhs.inner_; - } - - private: - template <class U, RawPtrTraits R> - friend class raw_ref; - - Inner inner_; -}; - -template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2> -PA_ALWAYS_INLINE bool operator==(const raw_ref<U, Traits1>& lhs, - const raw_ref<V, Traits2>& rhs) { - PA_RAW_PTR_CHECK(lhs.inner_); // Catch use-after-move. - PA_RAW_PTR_CHECK(rhs.inner_); // Catch use-after-move. - return lhs.inner_ == rhs.inner_; -} -template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2> -PA_ALWAYS_INLINE bool operator!=(const raw_ref<U, Traits1>& lhs, - const raw_ref<V, Traits2>& rhs) { - PA_RAW_PTR_CHECK(lhs.inner_); // Catch use-after-move. - PA_RAW_PTR_CHECK(rhs.inner_); // Catch use-after-move. - return lhs.inner_ != rhs.inner_; -} -template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2> -PA_ALWAYS_INLINE bool operator<(const raw_ref<U, Traits1>& lhs, - const raw_ref<V, Traits2>& rhs) { - PA_RAW_PTR_CHECK(lhs.inner_); // Catch use-after-move. - PA_RAW_PTR_CHECK(rhs.inner_); // Catch use-after-move. - return lhs.inner_ < rhs.inner_; -} -template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2> -PA_ALWAYS_INLINE bool operator>(const raw_ref<U, Traits1>& lhs, - const raw_ref<V, Traits2>& rhs) { - PA_RAW_PTR_CHECK(lhs.inner_); // Catch use-after-move. - PA_RAW_PTR_CHECK(rhs.inner_); // Catch use-after-move. - return lhs.inner_ > rhs.inner_; -} -template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2> -PA_ALWAYS_INLINE bool operator<=(const raw_ref<U, Traits1>& lhs, - const raw_ref<V, Traits2>& rhs) { - PA_RAW_PTR_CHECK(lhs.inner_); // Catch use-after-move. - PA_RAW_PTR_CHECK(rhs.inner_); // Catch use-after-move. - return lhs.inner_ <= rhs.inner_; -} -template <typename U, typename V, RawPtrTraits Traits1, RawPtrTraits Traits2> -PA_ALWAYS_INLINE bool operator>=(const raw_ref<U, Traits1>& lhs, - const raw_ref<V, Traits2>& rhs) { - PA_RAW_PTR_CHECK(lhs.inner_); // Catch use-after-move. - PA_RAW_PTR_CHECK(rhs.inner_); // Catch use-after-move. - return lhs.inner_ >= rhs.inner_; -} - -// CTAD deduction guide. -template <class T> -raw_ref(T&) -> raw_ref<T>; -template <class T> -raw_ref(const T&) -> raw_ref<const T>; - -// Template helpers for working with raw_ref<T>. -template <typename T> -struct IsRawRef : std::false_type {}; - -template <typename T, RawPtrTraits Traits> -struct IsRawRef<raw_ref<T, Traits>> : std::true_type {}; - -template <typename T> -inline constexpr bool IsRawRefV = IsRawRef<T>::value; - -template <typename T> -struct RemoveRawRef { - using type = T; -}; - -template <typename T, RawPtrTraits Traits> -struct RemoveRawRef<raw_ref<T, Traits>> { - using type = T; -}; - -template <typename T> -using RemoveRawRefT = typename RemoveRawRef<T>::type; - -} // namespace base - -using base::raw_ref; - -namespace std { - -// Override so set/map lookups do not create extra raw_ref. This also -// allows C++ references to be used for lookup. -template <typename T, base::RawPtrTraits Traits> -struct less<raw_ref<T, Traits>> { - using Impl = typename raw_ref<T, Traits>::Impl; - using is_transparent = void; - - bool operator()(const raw_ref<T, Traits>& lhs, - const raw_ref<T, Traits>& rhs) const { - Impl::IncrementLessCountForTest(); - return lhs < rhs; - } - - bool operator()(T& lhs, const raw_ref<T, Traits>& rhs) const { - Impl::IncrementLessCountForTest(); - return lhs < rhs; - } - - bool operator()(const raw_ref<T, Traits>& lhs, T& rhs) const { - Impl::IncrementLessCountForTest(); - return lhs < rhs; - } -}; - -#if defined(_LIBCPP_VERSION) -// Specialize std::pointer_traits. The latter is required to obtain the -// underlying raw pointer in the std::to_address(pointer) overload. -// Implementing the pointer_traits is the standard blessed way to customize -// `std::to_address(pointer)` in C++20 [3]. -// -// [1] https://wg21.link/pointer.traits.optmem - -template <typename T, ::base::RawPtrTraits Traits> -struct pointer_traits<::raw_ref<T, Traits>> { - using pointer = ::raw_ref<T, Traits>; - using element_type = T; - using difference_type = ptrdiff_t; - - template <typename U> - using rebind = ::raw_ref<U, Traits>; - - static constexpr pointer pointer_to(element_type& r) noexcept { - return pointer(r); - } - - static constexpr element_type* to_address(pointer p) noexcept { - // `raw_ref::get` is used instead of raw_ref::operator*`. It provides - // GetForExtraction rather rather than GetForDereference semantics (see - // raw_ptr.h). This should be used when we we don't know the memory will be - // accessed. - return &(p.get()); - } -}; -#endif // defined(_LIBCPP_VERSION) - -} // namespace std - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_POINTERS_RAW_REF_H_
diff --git a/base/allocator/partition_allocator/pointers/raw_ref_unittest.cc b/base/allocator/partition_allocator/pointers/raw_ref_unittest.cc deleted file mode 100644 index 9993cf3..0000000 --- a/base/allocator/partition_allocator/pointers/raw_ref_unittest.cc +++ /dev/null
@@ -1,1013 +0,0 @@ -// 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. - -#include "base/allocator/partition_allocator/pointers/raw_ref.h" - -#include <functional> -#include <type_traits> - -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/pointers/raw_ptr_test_support.h" -#include "base/memory/raw_ptr.h" -#include "base/test/gtest_util.h" -#include "testing/gtest/include/gtest/gtest.h" -#if BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) -#include "base/debug/asan_service.h" -#include "base/memory/raw_ptr_asan_service.h" -#endif // BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) - -namespace { - -class BaseClass {}; -class SubClass : public BaseClass {}; - -// raw_ref just defers to the superclass for implementations, so it -// can't add more data types. -static_assert(sizeof(raw_ref<int>) == sizeof(raw_ptr<int>)); - -// Since it can't hold null, raw_ref is not default-constructible. -static_assert(!std::is_default_constructible_v<raw_ref<int>>); -static_assert(!std::is_default_constructible_v<raw_ref<const int>>); - -// A mutable reference can only be constructed from a mutable lvalue reference. -static_assert(!std::is_constructible_v<raw_ref<int>, const int>); -static_assert(!std::is_constructible_v<raw_ref<int>, int>); -static_assert(!std::is_constructible_v<raw_ref<int>, const int&>); -static_assert(std::is_constructible_v<raw_ref<int>, int&>); -static_assert(!std::is_constructible_v<raw_ref<int>, const int*>); -static_assert(!std::is_constructible_v<raw_ref<int>, int*>); -static_assert(!std::is_constructible_v<raw_ref<int>, const int&&>); -static_assert(!std::is_constructible_v<raw_ref<int>, int&&>); -// Same for assignment. -static_assert(!std::is_assignable_v<raw_ref<int>, const int>); -static_assert(!std::is_assignable_v<raw_ref<int>, int>); -static_assert(!std::is_assignable_v<raw_ref<int>, const int&>); -static_assert(std::is_assignable_v<raw_ref<int>, int&>); -static_assert(!std::is_assignable_v<raw_ref<int>, const int*>); -static_assert(!std::is_assignable_v<raw_ref<int>, int*>); -static_assert(!std::is_assignable_v<raw_ref<int>, const int&&>); -static_assert(!std::is_assignable_v<raw_ref<int>, int&&>); - -// A const reference can be constructed from a const or mutable lvalue -// reference. -static_assert(!std::is_constructible_v<raw_ref<const int>, const int>); -static_assert(!std::is_constructible_v<raw_ref<const int>, int>); -static_assert(std::is_constructible_v<raw_ref<const int>, const int&>); -static_assert(std::is_constructible_v<raw_ref<const int>, int&>); -static_assert(!std::is_constructible_v<raw_ref<const int>, const int*>); -static_assert(!std::is_constructible_v<raw_ref<const int>, int*>); -static_assert(!std::is_constructible_v<raw_ref<const int>, const int&&>); -static_assert(!std::is_constructible_v<raw_ref<const int>, int&&>); -// Same for assignment. -static_assert(!std::is_assignable_v<raw_ref<const int>, const int>); -static_assert(!std::is_assignable_v<raw_ref<const int>, int>); -static_assert(std::is_assignable_v<raw_ref<const int>, const int&>); -static_assert(std::is_assignable_v<raw_ref<const int>, int&>); -static_assert(!std::is_assignable_v<raw_ref<const int>, const int*>); -static_assert(!std::is_assignable_v<raw_ref<const int>, int*>); -static_assert(!std::is_assignable_v<raw_ref<const int>, const int&&>); -static_assert(!std::is_assignable_v<raw_ref<const int>, int&&>); - -// Same trivial operations (or not) as raw_ptr<T>. -static_assert(std::is_trivially_constructible_v<raw_ref<int>, const int&> == - std::is_trivially_constructible_v<raw_ptr<int>, const int&>); -static_assert(std::is_trivially_destructible_v<raw_ref<int>> == - std::is_trivially_destructible_v<raw_ptr<int>>); -// But constructing from another raw_ref must check if it's internally null -// (which indicates use-after-move). -static_assert(!std::is_trivially_move_constructible_v<raw_ref<int>>); -static_assert(!std::is_trivially_move_assignable_v<raw_ref<int>>); -static_assert(!std::is_trivially_copy_constructible_v<raw_ref<int>>); -static_assert(!std::is_trivially_copy_assignable_v<raw_ref<int>>); - -// A raw_ref can be copied or moved. -static_assert(std::is_move_constructible_v<raw_ref<int>>); -static_assert(std::is_copy_constructible_v<raw_ref<int>>); -static_assert(std::is_move_assignable_v<raw_ref<int>>); -static_assert(std::is_copy_assignable_v<raw_ref<int>>); - -// A SubClass can be converted to a BaseClass. -static_assert(std::is_constructible_v<raw_ref<BaseClass>, raw_ref<SubClass>>); -static_assert( - std::is_constructible_v<raw_ref<BaseClass>, const raw_ref<SubClass>&>); -static_assert(std::is_constructible_v<raw_ref<BaseClass>, raw_ref<SubClass>&&>); -static_assert(std::is_assignable_v<raw_ref<BaseClass>, raw_ref<SubClass>>); -static_assert( - std::is_assignable_v<raw_ref<BaseClass>, const raw_ref<SubClass>&>); -static_assert(std::is_assignable_v<raw_ref<BaseClass>, raw_ref<SubClass>&&>); -// A BaseClass can't be implicitly downcasted. -static_assert(!std::is_constructible_v<raw_ref<SubClass>, raw_ref<BaseClass>>); -static_assert( - !std::is_constructible_v<raw_ref<SubClass>, const raw_ref<BaseClass>&>); -static_assert( - !std::is_constructible_v<raw_ref<SubClass>, raw_ref<BaseClass>&&>); -static_assert(!std::is_assignable_v<raw_ref<SubClass>, raw_ref<BaseClass>>); -static_assert( - !std::is_assignable_v<raw_ref<SubClass>, const raw_ref<BaseClass>&>); -static_assert(!std::is_assignable_v<raw_ref<SubClass>, raw_ref<BaseClass>&&>); - -// A raw_ref<BaseClass> can be constructed directly from a SubClass. -static_assert(std::is_constructible_v<raw_ref<BaseClass>, SubClass&>); -static_assert(std::is_assignable_v<raw_ref<BaseClass>, SubClass&>); -static_assert(std::is_constructible_v<raw_ref<const BaseClass>, SubClass&>); -static_assert(std::is_assignable_v<raw_ref<const BaseClass>, SubClass&>); -static_assert( - std::is_constructible_v<raw_ref<const BaseClass>, const SubClass&>); -static_assert(std::is_assignable_v<raw_ref<const BaseClass>, const SubClass&>); -// But a raw_ref<SubClass> can't be constructed from an implicit downcast from a -// BaseClass. -static_assert(!std::is_constructible_v<raw_ref<SubClass>, BaseClass&>); -static_assert(!std::is_assignable_v<raw_ref<SubClass>, BaseClass&>); -static_assert(!std::is_constructible_v<raw_ref<const SubClass>, BaseClass&>); -static_assert(!std::is_assignable_v<raw_ref<const SubClass>, BaseClass&>); -static_assert( - !std::is_constructible_v<raw_ref<const SubClass>, const BaseClass&>); -static_assert(!std::is_assignable_v<raw_ref<const SubClass>, const BaseClass&>); - -// A mutable reference can be converted to const reference. -static_assert(std::is_constructible_v<raw_ref<const int>, raw_ref<int>>); -static_assert(std::is_assignable_v<raw_ref<const int>, raw_ref<int>>); -// A const reference can't be converted to mutable. -static_assert(!std::is_constructible_v<raw_ref<int>, raw_ref<const int>>); -static_assert(!std::is_assignable_v<raw_ref<int>, raw_ref<const int>>); - -// The deref operator gives the internal reference. -static_assert(std::is_same_v<int&, decltype(*std::declval<raw_ref<int>>())>); -static_assert( - std::is_same_v<int&, decltype(*std::declval<const raw_ref<int>>())>); -static_assert(std::is_same_v<int&, decltype(*std::declval<raw_ref<int>&>())>); -static_assert( - std::is_same_v<int&, decltype(*std::declval<const raw_ref<int>&>())>); -static_assert(std::is_same_v<int&, decltype(*std::declval<raw_ref<int>&&>())>); -static_assert( - std::is_same_v<int&, decltype(*std::declval<const raw_ref<int>&&>())>); -// A const T is always returned as const. -static_assert( - std::is_same_v<const int&, decltype(*std::declval<raw_ref<const int>>())>); - -// The arrow operator gives a (non-null) pointer to the internal reference. -static_assert( - std::is_same_v<int*, decltype(std::declval<raw_ref<int>>().operator->())>); -static_assert( - std::is_same_v<const int*, - decltype(std::declval<raw_ref<const int>>().operator->())>); - -// Verify that raw_ref is a literal type, and its entire interface is constexpr. -// -// Constexpr destructors were introduced in C++20. PartitionAlloc's minimum -// supported C++ version is C++17, so raw_ref is not a literal type in C++17. -// Thus we only test for constexpr in C++20. -#if defined(__cpp_constexpr) && __cpp_constexpr >= 201907L -static_assert([]() constexpr { - struct IntBase {}; - struct Int : public IntBase { - int i = 0; - }; - - Int* i = new Int(); - { - raw_ref<Int> r(*i); // raw_ref(T&) - r = *i; // operator=(T&) - raw_ref<Int> r2(r); // raw_ref(const raw_ref&) - raw_ref<Int> r3(std::move(r2)); // raw_ref(raw_ref&&) - r2 = r; // operator=(const raw_ref&) - r3 = std::move(r2); // operator=(raw_ref&&) - r2 = r; // Reset after move. - [[maybe_unused]] raw_ref<IntBase> r5( - r2); // raw_ref(const raw_ref<Convertible>&) - [[maybe_unused]] raw_ref<IntBase> r6( - std::move(r2)); // raw_ref(raw_ref<Convertible>&&) - r2 = r; // Reset after move. - r5 = r2; // operator=(const raw_ref<Convertible>&) - r6 = std::move(r2); // operator=(raw_ref<Convertible>&&) - raw_ref<Int>::from_ptr(i); // from_ptr(T*) - (*r).i += 1; // operator*() - r.get().i += 1; // get() - r->i += 1; // operator->() - r2 = r; // Reset after move. - swap(r, r2); // swap() - } - delete i; - return true; -}()); -#endif - -TEST(RawRef, Construct) { - int i = 1; - auto r = raw_ref<int>(i); - EXPECT_EQ(&*r, &i); - auto cr = raw_ref<const int>(i); - EXPECT_EQ(&*cr, &i); - const int ci = 1; - auto cci = raw_ref<const int>(ci); - EXPECT_EQ(&*cci, &ci); -} - -TEST(RawRef, CopyConstruct) { - { - int i = 1; - auto r = raw_ref<int>(i); - EXPECT_EQ(&*r, &i); - auto r2 = raw_ref<int>(r); - EXPECT_EQ(&*r2, &i); - } - { - int i = 1; - auto r = raw_ref<const int>(i); - EXPECT_EQ(&*r, &i); - auto r2 = raw_ref<const int>(r); - EXPECT_EQ(&*r2, &i); - } -} - -TEST(RawRef, MoveConstruct) { - { - int i = 1; - auto r = raw_ref<int>(i); - EXPECT_EQ(&*r, &i); - auto r2 = raw_ref<int>(std::move(r)); - EXPECT_EQ(&*r2, &i); - } - { - int i = 1; - auto r = raw_ref<const int>(i); - EXPECT_EQ(&*r, &i); - auto r2 = raw_ref<const int>(std::move(r)); - EXPECT_EQ(&*r2, &i); - } -} - -TEST(RawRef, CopyAssign) { - { - int i = 1; - int j = 2; - auto r = raw_ref<int>(i); - EXPECT_EQ(&*r, &i); - auto rj = raw_ref<int>(j); - r = rj; - EXPECT_EQ(&*r, &j); - } - { - int i = 1; - int j = 2; - auto r = raw_ref<const int>(i); - EXPECT_EQ(&*r, &i); - auto rj = raw_ref<const int>(j); - r = rj; - EXPECT_EQ(&*r, &j); - } - { - int i = 1; - int j = 2; - auto r = raw_ref<const int>(i); - EXPECT_EQ(&*r, &i); - auto rj = raw_ref<int>(j); - r = rj; - EXPECT_EQ(&*r, &j); - } -} - -TEST(RawRef, CopyReassignAfterMove) { - int i = 1; - int j = 1; - auto r = raw_ref<int>(i); - auto r2 = std::move(r); - r2 = raw_ref<int>(j); - // Reassign to the moved-from `r` so it can be used again. - r = r2; - EXPECT_EQ(&*r, &j); -} - -TEST(RawRef, MoveAssign) { - { - int i = 1; - int j = 2; - auto r = raw_ref<int>(i); - EXPECT_EQ(&*r, &i); - r = raw_ref<int>(j); - EXPECT_EQ(&*r, &j); - } - { - int i = 1; - int j = 2; - auto r = raw_ref<const int>(i); - EXPECT_EQ(&*r, &i); - r = raw_ref<const int>(j); - EXPECT_EQ(&*r, &j); - } - { - int i = 1; - int j = 2; - auto r = raw_ref<const int>(i); - EXPECT_EQ(&*r, &i); - r = raw_ref<int>(j); - EXPECT_EQ(&*r, &j); - } -} - -TEST(RawRef, MoveReassignAfterMove) { - int i = 1; - int j = 1; - auto r = raw_ref<int>(i); - auto r2 = std::move(r); - // Reassign to the moved-from `r` so it can be used again. - r = raw_ref<int>(j); - EXPECT_EQ(&*r, &j); -} - -TEST(RawRef, CopyConstructUpCast) { - { - auto s = SubClass(); - auto r = raw_ref<SubClass>(s); - EXPECT_EQ(&*r, &s); - auto r2 = raw_ref<BaseClass>(r); - EXPECT_EQ(&*r2, &s); - } - { - auto s = SubClass(); - auto r = raw_ref<const SubClass>(s); - EXPECT_EQ(&*r, &s); - auto r2 = raw_ref<const BaseClass>(r); - EXPECT_EQ(&*r2, &s); - } -} - -TEST(RawRef, MoveConstructUpCast) { - { - auto s = SubClass(); - auto r = raw_ref<SubClass>(s); - EXPECT_EQ(&*r, &s); - auto r2 = raw_ref<BaseClass>(std::move(r)); - EXPECT_EQ(&*r2, &s); - } - { - auto s = SubClass(); - auto r = raw_ref<const SubClass>(s); - EXPECT_EQ(&*r, &s); - auto r2 = raw_ref<const BaseClass>(std::move(r)); - EXPECT_EQ(&*r2, &s); - } -} - -TEST(RawRef, FromPtr) { - int i = 42; - auto ref = raw_ref<int>::from_ptr(&i); - EXPECT_EQ(&i, &*ref); -} - -TEST(RawRef, CopyAssignUpCast) { - { - auto s = SubClass(); - auto r = raw_ref<SubClass>(s); - auto t = BaseClass(); - auto rt = raw_ref<BaseClass>(t); - rt = r; - EXPECT_EQ(&*rt, &s); - } - { - auto s = SubClass(); - auto r = raw_ref<const SubClass>(s); - auto t = BaseClass(); - auto rt = raw_ref<const BaseClass>(t); - rt = r; - EXPECT_EQ(&*rt, &s); - } - { - auto s = SubClass(); - auto r = raw_ref<SubClass>(s); - auto t = BaseClass(); - auto rt = raw_ref<const BaseClass>(t); - rt = r; - EXPECT_EQ(&*rt, &s); - } -} - -TEST(RawRef, MoveAssignUpCast) { - { - auto s = SubClass(); - auto r = raw_ref<SubClass>(s); - auto t = BaseClass(); - auto rt = raw_ref<BaseClass>(t); - rt = std::move(r); - EXPECT_EQ(&*rt, &s); - } - { - auto s = SubClass(); - auto r = raw_ref<const SubClass>(s); - auto t = BaseClass(); - auto rt = raw_ref<const BaseClass>(t); - rt = std::move(r); - EXPECT_EQ(&*rt, &s); - } - { - auto s = SubClass(); - auto r = raw_ref<SubClass>(s); - auto t = BaseClass(); - auto rt = raw_ref<const BaseClass>(t); - rt = std::move(r); - EXPECT_EQ(&*rt, &s); - } -} - -TEST(RawRef, Deref) { - int i; - auto r = raw_ref<int>(i); - EXPECT_EQ(&*r, &i); -} - -TEST(RawRef, Arrow) { - int i; - auto r = raw_ref<int>(i); - EXPECT_EQ(r.operator->(), &i); -} - -TEST(RawRef, Swap) { - int i; - int j; - auto ri = raw_ref<int>(i); - auto rj = raw_ref<int>(j); - swap(ri, rj); - EXPECT_EQ(&*ri, &j); - EXPECT_EQ(&*rj, &i); -} - -TEST(RawRef, Equals) { - int i = 1; - auto r1 = raw_ref<int>(i); - auto r2 = raw_ref<int>(i); - EXPECT_TRUE(r1 == r1); - EXPECT_TRUE(r1 == r2); - EXPECT_TRUE(r1 == i); - EXPECT_TRUE(i == r1); - int j = 1; - auto r3 = raw_ref<int>(j); - EXPECT_FALSE(r1 == r3); - EXPECT_FALSE(r1 == j); - EXPECT_FALSE(j == r1); -} - -TEST(RawRef, NotEquals) { - int i = 1; - auto r1 = raw_ref<int>(i); - int j = 1; - auto r2 = raw_ref<int>(j); - EXPECT_TRUE(r1 != r2); - EXPECT_TRUE(r1 != j); - EXPECT_TRUE(j != r1); - EXPECT_FALSE(r1 != r1); - EXPECT_FALSE(r2 != j); - EXPECT_FALSE(j != r2); -} - -TEST(RawRef, LessThan) { - int i[] = {1, 1}; - auto r1 = raw_ref<int>(i[0]); - auto r2 = raw_ref<int>(i[1]); - EXPECT_TRUE(r1 < r2); - EXPECT_TRUE(r1 < i[1]); - EXPECT_FALSE(i[1] < r1); - EXPECT_FALSE(r2 < r1); - EXPECT_FALSE(r2 < i[0]); - EXPECT_TRUE(i[0] < r2); - EXPECT_FALSE(r1 < r1); - EXPECT_FALSE(r1 < i[0]); - EXPECT_FALSE(i[0] < r1); -} - -TEST(RawRef, GreaterThan) { - int i[] = {1, 1}; - auto r1 = raw_ref<int>(i[0]); - auto r2 = raw_ref<int>(i[1]); - EXPECT_TRUE(r2 > r1); - EXPECT_FALSE(r1 > r2); - EXPECT_FALSE(r1 > i[1]); - EXPECT_TRUE(i[1] > r1); - EXPECT_FALSE(r2 > r2); - EXPECT_FALSE(r2 > i[1]); - EXPECT_FALSE(i[1] > r2); -} - -TEST(RawRef, LessThanOrEqual) { - int i[] = {1, 1}; - auto r1 = raw_ref<int>(i[0]); - auto r2 = raw_ref<int>(i[1]); - EXPECT_TRUE(r1 <= r2); - EXPECT_TRUE(r1 <= r1); - EXPECT_TRUE(r2 <= r2); - EXPECT_FALSE(r2 <= r1); - EXPECT_TRUE(r1 <= i[1]); - EXPECT_TRUE(r1 <= i[0]); - EXPECT_TRUE(r2 <= i[1]); - EXPECT_FALSE(r2 <= i[0]); - EXPECT_FALSE(i[1] <= r1); - EXPECT_TRUE(i[0] <= r1); - EXPECT_TRUE(i[1] <= r2); - EXPECT_TRUE(i[0] <= r2); -} - -TEST(RawRef, GreaterThanOrEqual) { - int i[] = {1, 1}; - auto r1 = raw_ref<int>(i[0]); - auto r2 = raw_ref<int>(i[1]); - EXPECT_TRUE(r2 >= r1); - EXPECT_TRUE(r1 >= r1); - EXPECT_TRUE(r2 >= r2); - EXPECT_FALSE(r1 >= r2); - EXPECT_TRUE(r2 >= i[0]); - EXPECT_TRUE(r1 >= i[0]); - EXPECT_TRUE(r2 >= i[1]); - EXPECT_FALSE(r1 >= i[1]); - EXPECT_FALSE(i[0] >= r2); - EXPECT_TRUE(i[0] >= r1); - EXPECT_TRUE(i[1] >= r2); - EXPECT_TRUE(i[1] >= r1); -} - -// Death Tests: If we're only using the no-op version of `raw_ptr` and -// have `!BUILDFLAG(PA_DCHECK_IS_ON)`, the `PA_RAW_PTR_CHECK()`s used in -// `raw_ref` evaluate to nothing. Therefore, death tests relying on -// these CHECKs firing are disabled in their absence. - -#if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) || \ - BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) || BUILDFLAG(PA_DCHECK_IS_ON) - -TEST(RawRefDeathTest, CopyConstructAfterMove) { - int i = 1; - auto r = raw_ref<int>(i); - auto r2 = std::move(r); - EXPECT_CHECK_DEATH({ [[maybe_unused]] auto r3 = r; }); -} - -TEST(RawRefDeathTest, MoveConstructAfterMove) { - int i = 1; - auto r = raw_ref<int>(i); - auto r2 = std::move(r); - EXPECT_CHECK_DEATH({ [[maybe_unused]] auto r3 = std::move(r); }); -} - -TEST(RawRefDeathTest, CopyAssignAfterMove) { - int i = 1; - auto r = raw_ref<int>(i); - auto r2 = std::move(r); - EXPECT_CHECK_DEATH({ r2 = r; }); -} - -TEST(RawRefDeathTest, MoveAssignAfterMove) { - int i = 1; - auto r = raw_ref<int>(i); - auto r2 = std::move(r); - EXPECT_CHECK_DEATH({ r2 = std::move(r); }); -} - -TEST(RawRefDeathTest, CopyConstructAfterMoveUpCast) { - auto s = SubClass(); - auto r = raw_ref<SubClass>(s); - auto moved = std::move(r); - EXPECT_CHECK_DEATH({ [[maybe_unused]] auto r2 = raw_ref<BaseClass>(r); }); -} - -TEST(RawRefDeathTest, MoveConstructAfterMoveUpCast) { - auto s = SubClass(); - auto r = raw_ref<SubClass>(s); - auto moved = std::move(r); - EXPECT_CHECK_DEATH( - { [[maybe_unused]] auto r2 = raw_ref<BaseClass>(std::move(r)); }); -} - -TEST(RawRefDeathTest, FromPtrWithNullptr) { - EXPECT_CHECK_DEATH({ raw_ref<int>::from_ptr(nullptr); }); -} - -TEST(RawRefDeathTest, CopyAssignAfterMoveUpCast) { - auto s = SubClass(); - auto r = raw_ref<const SubClass>(s); - auto t = BaseClass(); - auto rt = raw_ref<const BaseClass>(t); - auto moved = std::move(r); - EXPECT_CHECK_DEATH({ rt = r; }); -} - -TEST(RawRefDeathTest, MoveAssignAfterMoveUpCast) { - auto s = SubClass(); - auto r = raw_ref<const SubClass>(s); - auto t = BaseClass(); - auto rt = raw_ref<const BaseClass>(t); - auto moved = std::move(r); - EXPECT_CHECK_DEATH({ rt = std::move(r); }); -} - -TEST(RawRefDeathTest, DerefAfterMove) { - int i; - auto r = raw_ref<int>(i); - auto moved = std::move(r); - EXPECT_CHECK_DEATH({ r.operator*(); }); -} - -TEST(RawRefDeathTest, ArrowAfterMove) { - int i; - auto r = raw_ref<int>(i); - auto moved = std::move(r); - EXPECT_CHECK_DEATH({ r.operator->(); }); -} - -TEST(RawRefDeathTest, SwapAfterMove) { - { - int i; - auto ri = raw_ref<int>(i); - int j; - auto rj = raw_ref<int>(j); - - auto moved = std::move(ri); - EXPECT_CHECK_DEATH({ swap(ri, rj); }); - } - { - int i; - auto ri = raw_ref<int>(i); - int j; - auto rj = raw_ref<int>(j); - - auto moved = std::move(rj); - EXPECT_CHECK_DEATH({ swap(ri, rj); }); - } -} - -TEST(RawRefDeathTest, EqualsAfterMove) { - { - int i = 1; - auto r1 = raw_ref<int>(i); - auto r2 = raw_ref<int>(i); - auto moved = std::move(r1); - EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 == r2; }); - } - { - int i = 1; - auto r1 = raw_ref<int>(i); - auto r2 = raw_ref<int>(i); - auto moved = std::move(r2); - EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 == r2; }); - } - { - int i = 1; - auto r1 = raw_ref<int>(i); - auto moved = std::move(r1); - EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 == r1; }); - } -} - -TEST(RawRefDeathTest, NotEqualsAfterMove) { - { - int i = 1; - auto r1 = raw_ref<int>(i); - auto r2 = raw_ref<int>(i); - auto moved = std::move(r1); - EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 != r2; }); - } - { - int i = 1; - auto r1 = raw_ref<int>(i); - auto r2 = raw_ref<int>(i); - auto moved = std::move(r2); - EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 != r2; }); - } - { - int i = 1; - auto r1 = raw_ref<int>(i); - auto moved = std::move(r1); - EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 != r1; }); - } -} - -TEST(RawRefDeathTest, LessThanAfterMove) { - { - int i = 1; - auto r1 = raw_ref<int>(i); - auto r2 = raw_ref<int>(i); - auto moved = std::move(r1); - EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 < r2; }); - } - { - int i = 1; - auto r1 = raw_ref<int>(i); - auto r2 = raw_ref<int>(i); - auto moved = std::move(r2); - EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 < r2; }); - } - { - int i = 1; - auto r1 = raw_ref<int>(i); - auto moved = std::move(r1); - EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 < r1; }); - } -} - -TEST(RawRefDeathTest, GreaterThanAfterMove) { - { - int i = 1; - auto r1 = raw_ref<int>(i); - auto r2 = raw_ref<int>(i); - auto moved = std::move(r1); - EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 > r2; }); - } - { - int i = 1; - auto r1 = raw_ref<int>(i); - auto r2 = raw_ref<int>(i); - auto moved = std::move(r2); - EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 > r2; }); - } - { - int i = 1; - auto r1 = raw_ref<int>(i); - auto moved = std::move(r1); - EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 > r1; }); - } -} - -TEST(RawRefDeathTest, LessThanOrEqualAfterMove) { - { - int i = 1; - auto r1 = raw_ref<int>(i); - auto r2 = raw_ref<int>(i); - auto moved = std::move(r1); - EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 <= r2; }); - } - { - int i = 1; - auto r1 = raw_ref<int>(i); - auto r2 = raw_ref<int>(i); - auto moved = std::move(r2); - EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 <= r2; }); - } - { - int i = 1; - auto r1 = raw_ref<int>(i); - auto moved = std::move(r1); - EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 <= r1; }); - } -} - -TEST(RawRefDeathTest, GreaterThanOrEqualAfterMove) { - { - int i = 1; - auto r1 = raw_ref<int>(i); - auto r2 = raw_ref<int>(i); - auto moved = std::move(r1); - EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 >= r2; }); - } - { - int i = 1; - auto r1 = raw_ref<int>(i); - auto r2 = raw_ref<int>(i); - auto moved = std::move(r2); - EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 >= r2; }); - } - { - int i = 1; - auto r1 = raw_ref<int>(i); - auto moved = std::move(r1); - EXPECT_CHECK_DEATH({ [[maybe_unused]] bool b = r1 >= r1; }); - } -} - -#endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) || - // BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) || - // BUILDFLAG(PA_DCHECK_IS_ON) - -TEST(RawRef, CTAD) { - int i = 1; - auto r = raw_ref(i); - EXPECT_EQ(&*r, &i); -} - -TEST(RawRefPtr, CTADWithConst) { - std::string str; - struct S { - const raw_ref<const std::string> r; - }; - // Deduces as `raw_ref<std::string>`, for which the constructor call is valid - // making a mutable reference, and then converts to - // `raw_ref<const std::string>`. - S s1 = {.r = raw_ref(str)}; - // Deduces as raw_ref<const std::string>, for which the constructor call is - // valid from a const ref. - S s2 = {.r = raw_ref(static_cast<const std::string&>(str))}; - EXPECT_EQ(&*s1.r, &str); - EXPECT_EQ(&*s2.r, &str); -} - -// `kDisableHooks` matches what `CountingRawRef` does internally. -// `kUseCountingWrapperForTest` is removed, and `DisableHooks` is added. -using RawPtrCountingImpl = base::internal::RawPtrCountingImplWrapperForTest< - base::RawPtrTraits::kDisableHooks>; - -// `kDisableHooks | kMayDangle` matches what `CountingRawRefMayDangle` does -// internally. `kUseCountingWrapperForTest` is removed, `kDisableHooks` is -// added, and `kMayDangle` is kept. -using RawPtrCountingMayDangleImpl = - base::internal::RawPtrCountingImplWrapperForTest< - base::RawPtrTraits::kMayDangle | base::RawPtrTraits::kDisableHooks>; - -template <typename T> -using CountingRawRef = - raw_ref<T, base::RawPtrTraits::kUseCountingWrapperForTest>; -static_assert(std::is_same_v<CountingRawRef<int>::Impl, RawPtrCountingImpl>); - -template <typename T> -using CountingRawRefMayDangle = - raw_ref<T, - base::RawPtrTraits::kMayDangle | - base::RawPtrTraits::kUseCountingWrapperForTest>; -static_assert(std::is_same_v<CountingRawRefMayDangle<int>::Impl, - RawPtrCountingMayDangleImpl>); - -TEST(RawRef, StdLess) { - int i[] = {1, 1}; - { - RawPtrCountingImpl::ClearCounters(); - auto r1 = CountingRawRef<int>(i[0]); - auto r2 = CountingRawRef<int>(i[1]); - EXPECT_TRUE(std::less<CountingRawRef<int>>()(r1, r2)); - EXPECT_FALSE(std::less<CountingRawRef<int>>()(r2, r1)); - EXPECT_EQ(2, RawPtrCountingImpl::wrapped_ptr_less_cnt); - } - { - RawPtrCountingImpl::ClearCounters(); - const auto r1 = CountingRawRef<int>(i[0]); - const auto r2 = CountingRawRef<int>(i[1]); - EXPECT_TRUE(std::less<CountingRawRef<int>>()(r1, r2)); - EXPECT_FALSE(std::less<CountingRawRef<int>>()(r2, r1)); - EXPECT_EQ(2, RawPtrCountingImpl::wrapped_ptr_less_cnt); - } - { - RawPtrCountingImpl::ClearCounters(); - auto r1 = CountingRawRef<const int>(i[0]); - auto r2 = CountingRawRef<const int>(i[1]); - EXPECT_TRUE(std::less<CountingRawRef<const int>>()(r1, r2)); - EXPECT_FALSE(std::less<CountingRawRef<const int>>()(r2, r1)); - EXPECT_EQ(2, RawPtrCountingImpl::wrapped_ptr_less_cnt); - } - { - RawPtrCountingImpl::ClearCounters(); - auto r1 = CountingRawRef<int>(i[0]); - auto r2 = CountingRawRef<int>(i[1]); - EXPECT_TRUE(std::less<CountingRawRef<int>>()(r1, i[1])); - EXPECT_FALSE(std::less<CountingRawRef<int>>()(r2, i[0])); - EXPECT_EQ(2, RawPtrCountingImpl::wrapped_ptr_less_cnt); - } - { - RawPtrCountingImpl::ClearCounters(); - const auto r1 = CountingRawRef<int>(i[0]); - const auto r2 = CountingRawRef<int>(i[1]); - EXPECT_TRUE(std::less<CountingRawRef<int>>()(r1, i[1])); - EXPECT_FALSE(std::less<CountingRawRef<int>>()(r2, i[0])); - EXPECT_EQ(2, RawPtrCountingImpl::wrapped_ptr_less_cnt); - } - { - RawPtrCountingImpl::ClearCounters(); - auto r1 = CountingRawRef<const int>(i[0]); - auto r2 = CountingRawRef<const int>(i[1]); - EXPECT_TRUE(std::less<CountingRawRef<const int>>()(r1, i[1])); - EXPECT_FALSE(std::less<CountingRawRef<const int>>()(r2, i[0])); - EXPECT_EQ(2, RawPtrCountingImpl::wrapped_ptr_less_cnt); - } -} - -// Verifies that comparing `raw_ref`s with different underlying Traits -// is a valid utterance and primarily uses the `GetForComparison()` methods. -TEST(RawRef, OperatorsUseGetForComparison) { - int x = 123; - CountingRawRef<int> ref1(x); - CountingRawRefMayDangle<int> ref2(x); - - RawPtrCountingImpl::ClearCounters(); - RawPtrCountingMayDangleImpl::ClearCounters(); - - EXPECT_TRUE(ref1 == ref2); - EXPECT_FALSE(ref1 != ref2); - // The use of `PA_RAW_PTR_CHECK()`s to catch dangling references means - // that we can't actually readily specify whether there are 0 - // extractions (`CHECK()`s compiled out) or 2 extractions. - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_comparison_cnt = 2, - }), - CountersMatch()); - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingMayDangleImpl>{ - .get_for_comparison_cnt = 2, - }), - CountersMatch()); - - EXPECT_FALSE(ref1 < ref2); - EXPECT_FALSE(ref1 > ref2); - EXPECT_TRUE(ref1 <= ref2); - EXPECT_TRUE(ref1 >= ref2); - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_comparison_cnt = 6, - }), - CountersMatch()); - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingMayDangleImpl>{ - .get_for_comparison_cnt = 6, - }), - CountersMatch()); -} - -TEST(RawRef, CrossKindConversion) { - int x = 123; - CountingRawRef<int> ref1(x); - - RawPtrCountingImpl::ClearCounters(); - RawPtrCountingMayDangleImpl::ClearCounters(); - - CountingRawRefMayDangle<int> ref2(ref1); - CountingRawRefMayDangle<int> ref3(std::move(ref1)); // Falls back to copy. - - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 0, - .get_for_duplication_cnt = 2}), - CountersMatch()); - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingMayDangleImpl>{ - .wrap_raw_ptr_cnt = 0, .wrap_raw_ptr_for_dup_cnt = 2}), - CountersMatch()); -} - -TEST(RawRef, CrossKindAssignment) { - int x = 123; - CountingRawRef<int> ref1(x); - - CountingRawRefMayDangle<int> ref2(x); - CountingRawRefMayDangle<int> ref3(x); - - RawPtrCountingImpl::ClearCounters(); - RawPtrCountingMayDangleImpl::ClearCounters(); - ref2 = ref1; - ref3 = std::move(ref1); // Falls back to copy. - - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingImpl>{ - .get_for_dereference_cnt = 0, - .get_for_extraction_cnt = 0, - .get_for_duplication_cnt = 2}), - CountersMatch()); - EXPECT_THAT((CountingRawPtrExpectations<RawPtrCountingMayDangleImpl>{ - .wrap_raw_ptr_cnt = 0, .wrap_raw_ptr_for_dup_cnt = 2}), - CountersMatch()); -} - -#if BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) - -TEST(AsanBackupRefPtrImpl, RawRefGet) { - base::debug::AsanService::GetInstance()->Initialize(); - - if (!base::RawPtrAsanService::GetInstance().IsEnabled()) { - base::RawPtrAsanService::GetInstance().Configure( - base::EnableDereferenceCheck(true), base::EnableExtractionCheck(true), - base::EnableInstantiationCheck(true)); - } else { - ASSERT_TRUE( - base::RawPtrAsanService::GetInstance().is_dereference_check_enabled()); - ASSERT_TRUE( - base::RawPtrAsanService::GetInstance().is_extraction_check_enabled()); - ASSERT_TRUE(base::RawPtrAsanService::GetInstance() - .is_instantiation_check_enabled()); - } - - auto ptr = ::std::make_unique<int>(); - raw_ref<int> safe_ref(*ptr); - ptr.reset(); - - // This test is specifically to ensure that raw_ref.get() does not cause a - // dereference of the memory referred to by the reference. If there is a - // dereference, then this test will crash. - [[maybe_unused]] volatile int& ref = safe_ref.get(); -} - -TEST(AsanBackupRefPtrImpl, RawRefOperatorStar) { - base::debug::AsanService::GetInstance()->Initialize(); - - if (!base::RawPtrAsanService::GetInstance().IsEnabled()) { - base::RawPtrAsanService::GetInstance().Configure( - base::EnableDereferenceCheck(true), base::EnableExtractionCheck(true), - base::EnableInstantiationCheck(true)); - } else { - ASSERT_TRUE( - base::RawPtrAsanService::GetInstance().is_dereference_check_enabled()); - ASSERT_TRUE( - base::RawPtrAsanService::GetInstance().is_extraction_check_enabled()); - ASSERT_TRUE(base::RawPtrAsanService::GetInstance() - .is_instantiation_check_enabled()); - } - - auto ptr = ::std::make_unique<int>(); - raw_ref<int> safe_ref(*ptr); - ptr.reset(); - - // This test is specifically to ensure that &*raw_ref does not cause a - // dereference of the memory referred to by the reference. If there is a - // dereference, then this test will crash. - [[maybe_unused]] volatile int& ref = *safe_ref; -} - -#endif // BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) - -} // namespace
diff --git a/base/allocator/partition_allocator/pointers/raw_ref_unittest.nc b/base/allocator/partition_allocator/pointers/raw_ref_unittest.nc deleted file mode 100644 index 0285e70..0000000 --- a/base/allocator/partition_allocator/pointers/raw_ref_unittest.nc +++ /dev/null
@@ -1,82 +0,0 @@ -// 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. - -// This is a "No Compile Test" suite. -// http://dev.chromium.org/developers/testing/no-compile-tests - -#include "base/allocator/partition_allocator/pointers/raw_ref.h" - -namespace { - -#if defined(NCTEST_CROSS_KIND_CONVERSION_FROM_MAY_DANGLE) // [r"static assertion failed due to requirement '\(base::RawPtrTraits\)4U == \(\(base::RawPtrTraits\)5U | RawPtrTraits::kMayDangle\)'"] - -void WontCompile() { - int x = 123; - raw_ref<int, base::RawPtrTraits::kMayDangle> ref(x); - [[maybe_unused]] raw_ref<int> ref2(ref); -} - -#elif defined(NCTEST_CROSS_KIND_CONVERSION_FROM_DUMMY) // [r"static assertion failed due to requirement '\(base::RawPtrTraits\)5U == \(\(base::RawPtrTraits\)36U | RawPtrTraits::kMayDangle\)'"] - -void WontCompile() { - int x = 123; - raw_ref<int, base::RawPtrTraits::kDummyForTest> ref(x); - [[maybe_unused]] raw_ref<int, base::RawPtrTraits::kMayDangle> ref2(ref); -} - -#elif defined(NCTEST_CROSS_KIND_CONVERSION_MOVE_FROM_MAY_DANGLE) // [r"static assertion failed due to requirement '\(base::RawPtrTraits\)4U == \(\(base::RawPtrTraits\)5U | RawPtrTraits::kMayDangle\)'"] - -void WontCompile() { - int x = 123; - raw_ref<int, base::RawPtrTraits::kMayDangle> ref(x); - [[maybe_unused]] raw_ref<int> ref2(std::move(ref)); -} - -#elif defined(NCTEST_CROSS_KIND_CONVERSION_MOVE_FROM_DUMMY) // [r"static assertion failed due to requirement '\(base::RawPtrTraits\)5U == \(\(base::RawPtrTraits\)36U | RawPtrTraits::kMayDangle\)'"] - -void WontCompile() { - int x = 123; - raw_ref<int, base::RawPtrTraits::kDummyForTest> ref(x); - [[maybe_unused]] raw_ref<int, base::RawPtrTraits::kMayDangle> ref2(std::move(ref)); -} - -#elif defined(NCTEST_CROSS_KIND_ASSIGNMENT_FROM_MAY_DANGLE) // [r"static assertion failed due to requirement '\(base::RawPtrTraits\)4U == \(\(base::RawPtrTraits\)5U | RawPtrTraits::kMayDangle\)'"] - -void WontCompile() { - int x = 123; - raw_ref<int, base::RawPtrTraits::kMayDangle> ref(x); - raw_ref<int> ref2(x); - ref2 = ref; -} - -#elif defined(NCTEST_CROSS_KIND_ASSIGNMENT_FROM_DUMMY) // [r"static assertion failed due to requirement '\(base::RawPtrTraits\)5U == \(\(base::RawPtrTraits\)36U | RawPtrTraits::kMayDangle\)'"] - -void WontCompile() { - int x = 123; - raw_ref<int, base::RawPtrTraits::kDummyForTest> ref(x); - raw_ref<int, base::RawPtrTraits::kMayDangle> ref2(x); - ref2 = ref; -} - -#elif defined(NCTEST_CROSS_KIND_ASSIGNMENT_MOVE_FROM_MAY_DANGLE) // [r"static assertion failed due to requirement '\(base::RawPtrTraits\)4U == \(\(base::RawPtrTraits\)5U | RawPtrTraits::kMayDangle\)'"] - -void WontCompile() { - int x = 123; - raw_ref<int, base::RawPtrTraits::kMayDangle> ref(x); - raw_ref<int> ref2(x); - ref2 = std::move(ref); -} - -#elif defined(NCTEST_CROSS_KIND_ASSIGNMENT_MOVE_FROM_DUMMY) // [r"static assertion failed due to requirement '\(base::RawPtrTraits\)5U == \(\(base::RawPtrTraits\)36U | RawPtrTraits::kMayDangle\)'"] - -void WontCompile() { - int x = 123; - raw_ref<int, base::RawPtrTraits::kDummyForTest> ref(x); - raw_ref<int, base::RawPtrTraits::kMayDangle> ref2(x); - ref2 = std::move(ref); -} - -#endif - -} // namespace
diff --git a/base/allocator/partition_allocator/random.cc b/base/allocator/partition_allocator/random.cc deleted file mode 100644 index 6916909..0000000 --- a/base/allocator/partition_allocator/random.cc +++ /dev/null
@@ -1,70 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/random.h" - -#include <type_traits> - -#include "base/allocator/partition_allocator/partition_alloc_base/rand_util.h" -#include "base/allocator/partition_allocator/partition_alloc_base/thread_annotations.h" -#include "base/allocator/partition_allocator/partition_lock.h" - -namespace partition_alloc { - -class RandomGenerator { - public: - constexpr RandomGenerator() {} - - uint32_t RandomValue() { - ::partition_alloc::internal::ScopedGuard guard(lock_); - return GetGenerator()->RandUint32(); - } - - void SeedForTesting(uint64_t seed) { - ::partition_alloc::internal::ScopedGuard guard(lock_); - GetGenerator()->ReseedForTesting(seed); - } - - private: - ::partition_alloc::internal::Lock lock_ = {}; - bool initialized_ PA_GUARDED_BY(lock_) = false; - union { - internal::base::InsecureRandomGenerator instance_ PA_GUARDED_BY(lock_); - uint8_t instance_buffer_[sizeof( - internal::base::InsecureRandomGenerator)] PA_GUARDED_BY(lock_) = {}; - }; - - internal::base::InsecureRandomGenerator* GetGenerator() - PA_EXCLUSIVE_LOCKS_REQUIRED(lock_) { - if (!initialized_) { - new (instance_buffer_) internal::base::InsecureRandomGenerator(); - initialized_ = true; - } - return &instance_; - } -}; - -// Note: this is redundant, since the anonymous union is incompatible with a -// non-trivial default destructor. Not meant to be destructed anyway. -static_assert(std::is_trivially_destructible<RandomGenerator>::value, ""); - -namespace { - -RandomGenerator g_generator = {}; - -} // namespace - -namespace internal { - -uint32_t RandomValue() { - return g_generator.RandomValue(); -} - -} // namespace internal - -void SetMmapSeedForTesting(uint64_t seed) { - return g_generator.SeedForTesting(seed); -} - -} // namespace partition_alloc
diff --git a/base/allocator/partition_allocator/random.h b/base/allocator/partition_allocator/random.h deleted file mode 100644 index 8a3a85c..0000000 --- a/base/allocator/partition_allocator/random.h +++ /dev/null
@@ -1,31 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_RANDOM_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_RANDOM_H_ - -#include <stdint.h> - -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" - -namespace partition_alloc { - -namespace internal { - -// Returns a random value. The generator's internal state is initialized with -// `base::RandUint64` which is very unpredictable, but which is expensive due to -// the need to call into the kernel. Therefore this generator uses a fast, -// entirely user-space function after initialization. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) uint32_t RandomValue(); - -} // namespace internal - -// Sets the seed for the random number generator to a known value, to cause the -// RNG to generate a predictable sequence of outputs. May be called multiple -// times. -PA_COMPONENT_EXPORT(PARTITION_ALLOC) void SetMmapSeedForTesting(uint64_t seed); - -} // namespace partition_alloc - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_RANDOM_H_
diff --git a/base/allocator/partition_allocator/reservation_offset_table.cc b/base/allocator/partition_allocator/reservation_offset_table.cc deleted file mode 100644 index 6eac2bc..0000000 --- a/base/allocator/partition_allocator/reservation_offset_table.cc +++ /dev/null
@@ -1,19 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/reservation_offset_table.h" - -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" - -namespace partition_alloc::internal { - -#if BUILDFLAG(HAS_64_BIT_POINTERS) -ReservationOffsetTable::_PaddedReservationOffsetTables - ReservationOffsetTable::padded_reservation_offset_tables_ PA_PKEY_ALIGN; -#else -ReservationOffsetTable::_ReservationOffsetTable - ReservationOffsetTable::reservation_offset_table_; -#endif // BUILDFLAG(HAS_64_BIT_POINTERS) - -} // namespace partition_alloc::internal
diff --git a/base/allocator/partition_allocator/reservation_offset_table.h b/base/allocator/partition_allocator/reservation_offset_table.h deleted file mode 100644 index 80bfe90..0000000 --- a/base/allocator/partition_allocator/reservation_offset_table.h +++ /dev/null
@@ -1,287 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_RESERVATION_OFFSET_TABLE_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_RESERVATION_OFFSET_TABLE_H_ - -#include <cstddef> -#include <cstdint> -#include <limits> -#include <tuple> - -#include "base/allocator/partition_allocator/address_pool_manager.h" -#include "base/allocator/partition_allocator/partition_address_space.h" -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h" -#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "base/allocator/partition_allocator/pkey.h" -#include "base/allocator/partition_allocator/tagging.h" -#include "build/build_config.h" - -namespace partition_alloc::internal { - -static constexpr uint16_t kOffsetTagNotAllocated = - std::numeric_limits<uint16_t>::max(); -static constexpr uint16_t kOffsetTagNormalBuckets = - std::numeric_limits<uint16_t>::max() - 1; - -// The main purpose of the reservation offset table is to easily locate the -// direct map reservation start address for any given address. There is one -// entry in the table for each super page. -// -// When PartitionAlloc reserves an address region it is always aligned to -// super page boundary. However, in 32-bit mode, the size may not be aligned -// super-page-aligned, so it may look like this: -// |<--------- actual reservation size --------->| -// +----------+----------+-----+-----------+-----+ - - - + -// |SuperPage0|SuperPage1| ... |SuperPage K|SuperPage K+1| -// +----------+----------+-----+-----------+-----+ - - -.+ -// |<-X->|<-Y*)->| -// -// The table entries for reserved super pages say how many pages away from the -// reservation the super page is: -// +----------+----------+-----+-----------+-------------+ -// |Entry for |Entry for | ... |Entry for |Entry for | -// |SuperPage0|SuperPage1| |SuperPage K|SuperPage K+1| -// +----------+----------+-----+-----------+-------------+ -// | 0 | 1 | ... | K | K + 1 | -// +----------+----------+-----+-----------+-------------+ -// -// For an address Z, the reservation start can be found using this formula: -// ((Z >> kSuperPageShift) - (the entry for Z)) << kSuperPageShift -// -// kOffsetTagNotAllocated is a special tag denoting that the super page isn't -// allocated by PartitionAlloc and kOffsetTagNormalBuckets denotes that it is -// used for a normal-bucket allocation, not for a direct-map allocation. -// -// *) In 32-bit mode, Y is not used by PartitionAlloc, and cannot be used -// until X is unreserved, because PartitionAlloc always uses kSuperPageSize -// alignment when reserving address spaces. One can use check "is in pool?" -// to further determine which part of the super page is used by -// PartitionAlloc. This isn't a problem in 64-bit mode, where allocation -// granularity is kSuperPageSize. -class PA_COMPONENT_EXPORT(PARTITION_ALLOC) ReservationOffsetTable { - public: -#if BUILDFLAG(HAS_64_BIT_POINTERS) - // There is one reservation offset table per Pool in 64-bit mode. - static constexpr size_t kReservationOffsetTableCoverage = kPoolMaxSize; - static constexpr size_t kReservationOffsetTableLength = - kReservationOffsetTableCoverage >> kSuperPageShift; -#else - // The size of the reservation offset table should cover the entire 32-bit - // address space, one element per super page. - static constexpr uint64_t kGiB = 1024 * 1024 * 1024ull; - static constexpr size_t kReservationOffsetTableLength = - 4 * kGiB / kSuperPageSize; -#endif // BUILDFLAG(HAS_64_BIT_POINTERS) - static_assert(kReservationOffsetTableLength < kOffsetTagNormalBuckets, - "Offsets should be smaller than kOffsetTagNormalBuckets."); - - struct _ReservationOffsetTable { - // The number of table elements is less than MAX_UINT16, so the element type - // can be uint16_t. - static_assert( - kReservationOffsetTableLength <= std::numeric_limits<uint16_t>::max(), - "Length of the reservation offset table must be less than MAX_UINT16"); - uint16_t offsets[kReservationOffsetTableLength] = {}; - - constexpr _ReservationOffsetTable() { - for (uint16_t& offset : offsets) { - offset = kOffsetTagNotAllocated; - } - } - }; -#if BUILDFLAG(HAS_64_BIT_POINTERS) - // If pkey support is enabled, we need to pkey-tag the tables of the pkey - // pool. For this, we need to pad the tables so that the pkey ones start on a - // page boundary. - struct _PaddedReservationOffsetTables { - char pad_[PA_PKEY_ARRAY_PAD_SZ(_ReservationOffsetTable, kNumPools)] = {}; - struct _ReservationOffsetTable tables[kNumPools]; - char pad_after_[PA_PKEY_FILL_PAGE_SZ(sizeof(_ReservationOffsetTable))] = {}; - }; - static PA_CONSTINIT _PaddedReservationOffsetTables - padded_reservation_offset_tables_ PA_PKEY_ALIGN; -#else - // A single table for the entire 32-bit address space. - static PA_CONSTINIT struct _ReservationOffsetTable reservation_offset_table_; -#endif // BUILDFLAG(HAS_64_BIT_POINTERS) -}; - -#if BUILDFLAG(HAS_64_BIT_POINTERS) -PA_ALWAYS_INLINE uint16_t* GetReservationOffsetTable(pool_handle handle) { - PA_DCHECK(kNullPoolHandle < handle && handle <= kNumPools); - return ReservationOffsetTable::padded_reservation_offset_tables_ - .tables[handle - 1] - .offsets; -} - -PA_ALWAYS_INLINE const uint16_t* GetReservationOffsetTableEnd( - pool_handle handle) { - return GetReservationOffsetTable(handle) + - ReservationOffsetTable::kReservationOffsetTableLength; -} - -PA_ALWAYS_INLINE uint16_t* GetReservationOffsetTable(uintptr_t address) { - pool_handle handle = GetPool(address); - return GetReservationOffsetTable(handle); -} - -PA_ALWAYS_INLINE const uint16_t* GetReservationOffsetTableEnd( - uintptr_t address) { - pool_handle handle = GetPool(address); - return GetReservationOffsetTableEnd(handle); -} - -PA_ALWAYS_INLINE uint16_t* ReservationOffsetPointer(pool_handle pool, - uintptr_t offset_in_pool) { - size_t table_index = offset_in_pool >> kSuperPageShift; - PA_DCHECK(table_index < - ReservationOffsetTable::kReservationOffsetTableLength); - return GetReservationOffsetTable(pool) + table_index; -} -#else // BUILDFLAG(HAS_64_BIT_POINTERS) -PA_ALWAYS_INLINE uint16_t* GetReservationOffsetTable(uintptr_t address) { - return ReservationOffsetTable::reservation_offset_table_.offsets; -} - -PA_ALWAYS_INLINE const uint16_t* GetReservationOffsetTableEnd( - uintptr_t address) { - return ReservationOffsetTable::reservation_offset_table_.offsets + - ReservationOffsetTable::kReservationOffsetTableLength; -} -#endif // BUILDFLAG(HAS_64_BIT_POINTERS) - -PA_ALWAYS_INLINE uint16_t* ReservationOffsetPointer(uintptr_t address) { -#if BUILDFLAG(HAS_64_BIT_POINTERS) - // In 64-bit mode, find the owning Pool and compute the offset from its base. - auto [pool, offset] = GetPoolAndOffset(address); - return ReservationOffsetPointer(pool, offset); -#else - size_t table_index = address >> kSuperPageShift; - PA_DCHECK(table_index < - ReservationOffsetTable::kReservationOffsetTableLength); - return GetReservationOffsetTable(address) + table_index; -#endif -} - -PA_ALWAYS_INLINE uintptr_t ComputeReservationStart(uintptr_t address, - uint16_t* offset_ptr) { - return (address & kSuperPageBaseMask) - - (static_cast<size_t>(*offset_ptr) << kSuperPageShift); -} - -// If the given address doesn't point to direct-map allocated memory, -// returns 0. -PA_ALWAYS_INLINE uintptr_t GetDirectMapReservationStart(uintptr_t address) { -#if BUILDFLAG(PA_DCHECK_IS_ON) - bool is_in_brp_pool = IsManagedByPartitionAllocBRPPool(address); - bool is_in_regular_pool = IsManagedByPartitionAllocRegularPool(address); - bool is_in_configurable_pool = - IsManagedByPartitionAllocConfigurablePool(address); -#if BUILDFLAG(ENABLE_PKEYS) - bool is_in_pkey_pool = IsManagedByPartitionAllocPkeyPool(address); -#endif - - // When ENABLE_BACKUP_REF_PTR_SUPPORT is off, BRP pool isn't used. -#if !BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - PA_DCHECK(!is_in_brp_pool); -#endif -#endif // BUILDFLAG(PA_DCHECK_IS_ON) - uint16_t* offset_ptr = ReservationOffsetPointer(address); - PA_DCHECK(*offset_ptr != kOffsetTagNotAllocated); - if (*offset_ptr == kOffsetTagNormalBuckets) { - return 0; - } - uintptr_t reservation_start = ComputeReservationStart(address, offset_ptr); -#if BUILDFLAG(PA_DCHECK_IS_ON) - // MSVC workaround: the preprocessor seems to choke on an `#if` embedded - // inside another macro (PA_DCHECK). -#if !BUILDFLAG(HAS_64_BIT_POINTERS) - constexpr size_t kBRPOffset = - AddressPoolManagerBitmap::kBytesPer1BitOfBRPPoolBitmap * - AddressPoolManagerBitmap::kGuardOffsetOfBRPPoolBitmap; -#else - constexpr size_t kBRPOffset = 0ull; -#endif // !BUILDFLAG(HAS_64_BIT_POINTERS) - // Make sure the reservation start is in the same pool as |address|. - // In the 32-bit mode, the beginning of a reservation may be excluded - // from the BRP pool, so shift the pointer. The other pools don't have - // this logic. - PA_DCHECK(is_in_brp_pool == - IsManagedByPartitionAllocBRPPool(reservation_start + kBRPOffset)); - PA_DCHECK(is_in_regular_pool == - IsManagedByPartitionAllocRegularPool(reservation_start)); - PA_DCHECK(is_in_configurable_pool == - IsManagedByPartitionAllocConfigurablePool(reservation_start)); -#if BUILDFLAG(ENABLE_PKEYS) - PA_DCHECK(is_in_pkey_pool == - IsManagedByPartitionAllocPkeyPool(reservation_start)); -#endif - PA_DCHECK(*ReservationOffsetPointer(reservation_start) == 0); -#endif // BUILDFLAG(PA_DCHECK_IS_ON) - - return reservation_start; -} - -#if BUILDFLAG(HAS_64_BIT_POINTERS) -// If the given address doesn't point to direct-map allocated memory, -// returns 0. -// This variant has better performance than the regular one on 64-bit builds if -// the Pool that an allocation belongs to is known. -PA_ALWAYS_INLINE uintptr_t -GetDirectMapReservationStart(uintptr_t address, - pool_handle pool, - uintptr_t offset_in_pool) { - PA_DCHECK(AddressPoolManager::GetInstance().GetPoolBaseAddress(pool) + - offset_in_pool == - address); - uint16_t* offset_ptr = ReservationOffsetPointer(pool, offset_in_pool); - PA_DCHECK(*offset_ptr != kOffsetTagNotAllocated); - if (*offset_ptr == kOffsetTagNormalBuckets) { - return 0; - } - uintptr_t reservation_start = ComputeReservationStart(address, offset_ptr); - PA_DCHECK(*ReservationOffsetPointer(reservation_start) == 0); - return reservation_start; -} -#endif // BUILDFLAG(HAS_64_BIT_POINTERS) - -// Returns true if |address| is the beginning of the first super page of a -// reservation, i.e. either a normal bucket super page, or the first super page -// of direct map. -// |address| must belong to an allocated super page. -PA_ALWAYS_INLINE bool IsReservationStart(uintptr_t address) { - uint16_t* offset_ptr = ReservationOffsetPointer(address); - PA_DCHECK(*offset_ptr != kOffsetTagNotAllocated); - return ((*offset_ptr == kOffsetTagNormalBuckets) || (*offset_ptr == 0)) && - (address % kSuperPageSize == 0); -} - -// Returns true if |address| belongs to a normal bucket super page. -PA_ALWAYS_INLINE bool IsManagedByNormalBuckets(uintptr_t address) { - uint16_t* offset_ptr = ReservationOffsetPointer(address); - return *offset_ptr == kOffsetTagNormalBuckets; -} - -// Returns true if |address| belongs to a direct map region. -PA_ALWAYS_INLINE bool IsManagedByDirectMap(uintptr_t address) { - uint16_t* offset_ptr = ReservationOffsetPointer(address); - return *offset_ptr != kOffsetTagNormalBuckets && - *offset_ptr != kOffsetTagNotAllocated; -} - -// Returns true if |address| belongs to a normal bucket super page or a direct -// map region, i.e. belongs to an allocated super page. -PA_ALWAYS_INLINE bool IsManagedByNormalBucketsOrDirectMap(uintptr_t address) { - uint16_t* offset_ptr = ReservationOffsetPointer(address); - return *offset_ptr != kOffsetTagNotAllocated; -} - -} // namespace partition_alloc::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_RESERVATION_OFFSET_TABLE_H_
diff --git a/base/allocator/partition_allocator/reverse_bytes.h b/base/allocator/partition_allocator/reverse_bytes.h deleted file mode 100644 index 2443d47..0000000 --- a/base/allocator/partition_allocator/reverse_bytes.h +++ /dev/null
@@ -1,49 +0,0 @@ -// 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. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_REVERSE_BYTES_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_REVERSE_BYTES_H_ - -// This header defines drop-in constexpr replacements for the -// byte-reversing routines that we used from `//base/sys_byteorder.h`. -// They will be made moot by C++23's <endian> header or by C++20's -// <bit> header. - -#include <cstdint> - -#include "base/allocator/partition_allocator/partition_alloc_config.h" -#include "build/build_config.h" - -namespace partition_alloc::internal { - -constexpr uint32_t ReverseFourBytes(uint32_t value) { -#if PA_CONFIG(IS_NONCLANG_MSVC) - return value >> 24 | (value >> 8 & 0xff00) | (value & 0xff00) << 8 | - value << 24; -#else - return __builtin_bswap32(value); -#endif // PA_CONFIG(IS_NONCLANG_MSVC) -} - -constexpr uint64_t ReverseEightBytes(uint64_t value) { -#if PA_CONFIG(IS_NONCLANG_MSVC) - return value >> 56 | (value >> 40 & 0xff00) | (value >> 24 & 0xff0000) | - (value >> 8 & 0xff000000) | (value & 0xff000000) << 8 | - (value & 0xff0000) << 24 | (value & 0xff00) << 40 | - (value & 0xff) << 56; -#else - return __builtin_bswap64(value); -#endif // PA_CONFIG(IS_NONCLANG_MSVC) -} - -constexpr uintptr_t ReverseBytes(uintptr_t value) { - if (sizeof(uintptr_t) == 4) { - return ReverseFourBytes(static_cast<uint32_t>(value)); - } - return ReverseEightBytes(static_cast<uint64_t>(value)); -} - -} // namespace partition_alloc::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_REVERSE_BYTES_H_
diff --git a/base/allocator/partition_allocator/reverse_bytes_unittest.cc b/base/allocator/partition_allocator/reverse_bytes_unittest.cc deleted file mode 100644 index ecd2a16..0000000 --- a/base/allocator/partition_allocator/reverse_bytes_unittest.cc +++ /dev/null
@@ -1,26 +0,0 @@ -// 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. - -#include "base/allocator/partition_allocator/reverse_bytes.h" - -#include <cstdint> - -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace partition_alloc::internal { -namespace { - -TEST(ReverseBytes, DeadBeefScramble) { - if (sizeof(uintptr_t) == 4) { - EXPECT_EQ(ReverseBytes(uintptr_t{0xefbeadde}), 0xdeadbeef); - } else { - // Hacky kludge to escape the compiler from immediately noticing that - // this won't fit into a uintptr_t when it's four bytes. - EXPECT_EQ(ReverseBytes(uint64_t{0xffeeddccefbeadde}), 0xdeadbeefccddeeff); - } -} - -} // namespace -} // namespace partition_alloc::internal
diff --git a/base/allocator/partition_allocator/shim/DEPS b/base/allocator/partition_allocator/shim/DEPS deleted file mode 100644 index da271c5..0000000 --- a/base/allocator/partition_allocator/shim/DEPS +++ /dev/null
@@ -1,39 +0,0 @@ -# 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. - -# This file is meant to be temporary during we migrate allocator_shim code -# into partition_allocator/. This file will be removed away once the -# migration gets done. - -include_rules = [ - "+base/allocator/buildflags.h", - "+base/allocator/early_zone_registration_mac.h", - "+base/allocator/partition_alloc_features.h", - "+base/allocator/partition_allocator/partition_alloc_base", - "+base/base_export.h", - "+base/functional/bind.h", - "+base/logging.h", - "+base/mac/mach_logging.h", - "+base/memory/nonscannable_memory.h", - "+base/memory/page_size.h", - "+base/process/memory.h", - "+base/synchronization/lock.h", - "+base/task/sequenced_task_runner.h", - "+base/threading/platform_thread.h", - "+base/time/time.h", - "+base/types/strong_alias.h", - "+build/chromecast_buildflags.h", - "+third_party/apple_apsl/CFBase.h", - "+third_party/apple_apsl/malloc.h", -] - -specific_include_rules = { - "allocator_shim_unittest\.cc$": [ - "+base/synchronization/waitable_event.h", - "+base/threading/thread_local.h", - ], - "allocator_interception_mac_unittest\.mm$": [ - "+testing/gtest/include/gtest/gtest.h", - ], -}
diff --git a/base/allocator/partition_allocator/shim/allocator_interception_mac.h b/base/allocator/partition_allocator/shim/allocator_interception_mac.h deleted file mode 100644 index c34937f..0000000 --- a/base/allocator/partition_allocator/shim/allocator_interception_mac.h +++ /dev/null
@@ -1,63 +0,0 @@ -// Copyright 2017 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_INTERCEPTION_MAC_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_INTERCEPTION_MAC_H_ - -#include <stddef.h> - -#include "base/base_export.h" -#include "third_party/apple_apsl/malloc.h" - -namespace allocator_shim { - -struct MallocZoneFunctions; - -// This initializes AllocatorDispatch::default_dispatch by saving pointers to -// the functions in the current default malloc zone. This must be called before -// the default malloc zone is changed to have its intended effect. -void InitializeDefaultDispatchToMacAllocator(); - -// Saves the function pointers currently used by the default zone. -void StoreFunctionsForDefaultZone(); - -// Same as StoreFunctionsForDefaultZone, but for all malloc zones. -void StoreFunctionsForAllZones(); - -// For all malloc zones that have been stored, replace their functions with -// |functions|. -void ReplaceFunctionsForStoredZones(const MallocZoneFunctions* functions); - -extern bool g_replaced_default_zone; - -// Calls the original implementation of malloc/calloc prior to interception. -bool UncheckedMallocMac(size_t size, void** result); -bool UncheckedCallocMac(size_t num_items, size_t size, void** result); - -// Intercepts calls to default and purgeable malloc zones. Intercepts Core -// Foundation and Objective-C allocations. -// Has no effect on the default malloc zone if the allocator shim already -// performs that interception. -BASE_EXPORT void InterceptAllocationsMac(); - -// Updates all malloc zones to use their original functions. -// Also calls ClearAllMallocZonesForTesting. -BASE_EXPORT void UninterceptMallocZonesForTesting(); - -// Returns true if allocations are successfully being intercepted for all malloc -// zones. -bool AreMallocZonesIntercepted(); - -// Periodically checks for, and shims new malloc zones. Stops checking after 1 -// minute. -BASE_EXPORT void PeriodicallyShimNewMallocZones(); - -// Exposed for testing. -BASE_EXPORT void ShimNewMallocZones(); -BASE_EXPORT void ReplaceZoneFunctions(ChromeMallocZone* zone, - const MallocZoneFunctions* functions); - -} // namespace allocator_shim - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_INTERCEPTION_MAC_H_
diff --git a/base/allocator/partition_allocator/shim/allocator_interception_mac.mm b/base/allocator/partition_allocator/shim/allocator_interception_mac.mm deleted file mode 100644 index 1db2f0c..0000000 --- a/base/allocator/partition_allocator/shim/allocator_interception_mac.mm +++ /dev/null
@@ -1,631 +0,0 @@ -// Copyright 2017 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file contains all the logic necessary to intercept allocations on -// macOS. "malloc zones" are an abstraction that allows the process to intercept -// all malloc-related functions. There is no good mechanism [short of -// interposition] to determine new malloc zones are added, so there's no clean -// mechanism to intercept all malloc zones. This file contains logic to -// intercept the default and purgeable zones, which always exist. A cursory -// review of Chrome seems to imply that non-default zones are almost never used. -// -// This file also contains logic to intercept Core Foundation and Objective-C -// allocations. The implementations forward to the default malloc zone, so the -// only reason to intercept these calls is to re-label OOM crashes with slightly -// more details. - -#include "base/allocator/partition_allocator/shim/allocator_interception_mac.h" - -#include <CoreFoundation/CoreFoundation.h> -#import <Foundation/Foundation.h> -#include <errno.h> -#include <mach/mach.h> -#import <objc/runtime.h> -#include <stddef.h> - -#include <algorithm> -#include <new> - -#include "base/allocator/partition_allocator/partition_alloc_base/bits.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/shim/malloc_zone_functions_mac.h" -#include "base/functional/bind.h" -#include "base/logging.h" -#include "base/mac/mach_logging.h" -#include "base/process/memory.h" -#import "base/task/sequenced_task_runner.h" -#include "base/task/sequenced_task_runner.h" -#include "base/time/time.h" -#include "build/build_config.h" -#include "third_party/apple_apsl/CFBase.h" - -#if BUILDFLAG(IS_IOS) -#include "base/allocator/partition_allocator/partition_alloc_base/ios/ios_util.h" -#else -#include "base/allocator/partition_allocator/partition_alloc_base/mac/mac_util.h" -#endif - -namespace allocator_shim { - -bool g_replaced_default_zone = false; - -namespace { - -bool g_oom_killer_enabled; -bool g_allocator_shims_failed_to_install; - -// Starting with Mac OS X 10.7, the zone allocators set up by the system are -// read-only, to prevent them from being overwritten in an attack. However, -// blindly unprotecting and reprotecting the zone allocators fails with -// GuardMalloc because GuardMalloc sets up its zone allocator using a block of -// memory in its bss. Explicit saving/restoring of the protection is required. -// -// This function takes a pointer to a malloc zone, de-protects it if necessary, -// and returns (in the out parameters) a region of memory (if any) to be -// re-protected when modifications are complete. This approach assumes that -// there is no contention for the protection of this memory. -// -// Returns true if the malloc zone was properly de-protected, or false -// otherwise. If this function returns false, the out parameters are invalid and -// the region does not need to be re-protected. -bool DeprotectMallocZone(ChromeMallocZone* default_zone, - vm_address_t* reprotection_start, - vm_size_t* reprotection_length, - vm_prot_t* reprotection_value) { - mach_port_t unused; - *reprotection_start = reinterpret_cast<vm_address_t>(default_zone); - struct vm_region_basic_info_64 info; - mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64; - kern_return_t result = - vm_region_64(mach_task_self(), reprotection_start, reprotection_length, - VM_REGION_BASIC_INFO_64, - reinterpret_cast<vm_region_info_t>(&info), &count, &unused); - if (result != KERN_SUCCESS) { - MACH_LOG(ERROR, result) << "vm_region_64"; - return false; - } - - // The kernel always returns a null object for VM_REGION_BASIC_INFO_64, but - // balance it with a deallocate in case this ever changes. See - // the VM_REGION_BASIC_INFO_64 case in vm_map_region() in 10.15's - // https://opensource.apple.com/source/xnu/xnu-6153.11.26/osfmk/vm/vm_map.c . - mach_port_deallocate(mach_task_self(), unused); - - if (!(info.max_protection & VM_PROT_WRITE)) { - LOG(ERROR) << "Invalid max_protection " << info.max_protection; - return false; - } - - // Does the region fully enclose the zone pointers? Possibly unwarranted - // simplification used: using the size of a full version 10 malloc zone rather - // than the actual smaller size if the passed-in zone is not version 10. - DCHECK(*reprotection_start <= reinterpret_cast<vm_address_t>(default_zone)); - vm_size_t zone_offset = reinterpret_cast<vm_address_t>(default_zone) - - reinterpret_cast<vm_address_t>(*reprotection_start); - DCHECK(zone_offset + sizeof(ChromeMallocZone) <= *reprotection_length); - - if (info.protection & VM_PROT_WRITE) { - // No change needed; the zone is already writable. - *reprotection_start = 0; - *reprotection_length = 0; - *reprotection_value = VM_PROT_NONE; - } else { - *reprotection_value = info.protection; - result = - vm_protect(mach_task_self(), *reprotection_start, *reprotection_length, - false, info.protection | VM_PROT_WRITE); - if (result != KERN_SUCCESS) { - MACH_LOG(ERROR, result) << "vm_protect"; - return false; - } - } - return true; -} - -#if !defined(ADDRESS_SANITIZER) - -MallocZoneFunctions g_old_zone; -MallocZoneFunctions g_old_purgeable_zone; - -#if !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - -void* oom_killer_malloc(struct _malloc_zone_t* zone, size_t size) { - void* result = g_old_zone.malloc(zone, size); - if (!result && size) - partition_alloc::TerminateBecauseOutOfMemory(size); - return result; -} - -void* oom_killer_calloc(struct _malloc_zone_t* zone, - size_t num_items, - size_t size) { - void* result = g_old_zone.calloc(zone, num_items, size); - if (!result && num_items && size) - partition_alloc::TerminateBecauseOutOfMemory(num_items * size); - return result; -} - -void* oom_killer_valloc(struct _malloc_zone_t* zone, size_t size) { - void* result = g_old_zone.valloc(zone, size); - if (!result && size) - partition_alloc::TerminateBecauseOutOfMemory(size); - return result; -} - -void oom_killer_free(struct _malloc_zone_t* zone, void* ptr) { - g_old_zone.free(zone, ptr); -} - -void* oom_killer_realloc(struct _malloc_zone_t* zone, void* ptr, size_t size) { - void* result = g_old_zone.realloc(zone, ptr, size); - if (!result && size) - partition_alloc::TerminateBecauseOutOfMemory(size); - return result; -} - -void* oom_killer_memalign(struct _malloc_zone_t* zone, - size_t alignment, - size_t size) { - void* result = g_old_zone.memalign(zone, alignment, size); - // Only die if posix_memalign would have returned ENOMEM, since there are - // other reasons why null might be returned. See posix_memalign() in 10.15's - // https://opensource.apple.com/source/libmalloc/libmalloc-283/src/malloc.c . - if (!result && size && alignment >= sizeof(void*) && - partition_alloc::internal::base::bits::IsPowerOfTwo(alignment)) { - partition_alloc::TerminateBecauseOutOfMemory(size); - } - return result; -} - -#endif // !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - -void* oom_killer_malloc_purgeable(struct _malloc_zone_t* zone, size_t size) { - void* result = g_old_purgeable_zone.malloc(zone, size); - if (!result && size) - partition_alloc::TerminateBecauseOutOfMemory(size); - return result; -} - -void* oom_killer_calloc_purgeable(struct _malloc_zone_t* zone, - size_t num_items, - size_t size) { - void* result = g_old_purgeable_zone.calloc(zone, num_items, size); - if (!result && num_items && size) - partition_alloc::TerminateBecauseOutOfMemory(num_items * size); - return result; -} - -void* oom_killer_valloc_purgeable(struct _malloc_zone_t* zone, size_t size) { - void* result = g_old_purgeable_zone.valloc(zone, size); - if (!result && size) - partition_alloc::TerminateBecauseOutOfMemory(size); - return result; -} - -void oom_killer_free_purgeable(struct _malloc_zone_t* zone, void* ptr) { - g_old_purgeable_zone.free(zone, ptr); -} - -void* oom_killer_realloc_purgeable(struct _malloc_zone_t* zone, - void* ptr, - size_t size) { - void* result = g_old_purgeable_zone.realloc(zone, ptr, size); - if (!result && size) - partition_alloc::TerminateBecauseOutOfMemory(size); - return result; -} - -void* oom_killer_memalign_purgeable(struct _malloc_zone_t* zone, - size_t alignment, - size_t size) { - void* result = g_old_purgeable_zone.memalign(zone, alignment, size); - // Only die if posix_memalign would have returned ENOMEM, since there are - // other reasons why null might be returned. See posix_memalign() in 10.15's - // https://opensource.apple.com/source/libmalloc/libmalloc-283/src/malloc.c . - if (!result && size && alignment >= sizeof(void*) && - partition_alloc::internal::base::bits::IsPowerOfTwo(alignment)) { - partition_alloc::TerminateBecauseOutOfMemory(size); - } - return result; -} - -#endif // !defined(ADDRESS_SANITIZER) - -#if !defined(ADDRESS_SANITIZER) - -// === Core Foundation CFAllocators === - -bool CanGetContextForCFAllocator() { -#if BUILDFLAG(IS_IOS) - return !partition_alloc::internal::base::ios::IsRunningOnOrLater(17, 0, 0); -#else - return !partition_alloc::internal::base::mac::IsOSLaterThan13_DontCallThis(); -#endif -} - -CFAllocatorContext* ContextForCFAllocator(CFAllocatorRef allocator) { - ChromeCFAllocatorLions* our_allocator = const_cast<ChromeCFAllocatorLions*>( - reinterpret_cast<const ChromeCFAllocatorLions*>(allocator)); - return &our_allocator->_context; -} - -CFAllocatorAllocateCallBack g_old_cfallocator_system_default; -CFAllocatorAllocateCallBack g_old_cfallocator_malloc; -CFAllocatorAllocateCallBack g_old_cfallocator_malloc_zone; - -void* oom_killer_cfallocator_system_default(CFIndex alloc_size, - CFOptionFlags hint, - void* info) { - void* result = g_old_cfallocator_system_default(alloc_size, hint, info); - if (!result) - partition_alloc::TerminateBecauseOutOfMemory( - static_cast<size_t>(alloc_size)); - return result; -} - -void* oom_killer_cfallocator_malloc(CFIndex alloc_size, - CFOptionFlags hint, - void* info) { - void* result = g_old_cfallocator_malloc(alloc_size, hint, info); - if (!result) - partition_alloc::TerminateBecauseOutOfMemory( - static_cast<size_t>(alloc_size)); - return result; -} - -void* oom_killer_cfallocator_malloc_zone(CFIndex alloc_size, - CFOptionFlags hint, - void* info) { - void* result = g_old_cfallocator_malloc_zone(alloc_size, hint, info); - if (!result) - partition_alloc::TerminateBecauseOutOfMemory( - static_cast<size_t>(alloc_size)); - return result; -} - -#endif // !defined(ADDRESS_SANITIZER) - -// === Cocoa NSObject allocation === - -typedef id (*allocWithZone_t)(id, SEL, NSZone*); -allocWithZone_t g_old_allocWithZone; - -id oom_killer_allocWithZone(id self, SEL _cmd, NSZone* zone) { - id result = g_old_allocWithZone(self, _cmd, zone); - if (!result) - partition_alloc::TerminateBecauseOutOfMemory(0); - return result; -} - -void UninterceptMallocZoneForTesting(struct _malloc_zone_t* zone) { - ChromeMallocZone* chrome_zone = reinterpret_cast<ChromeMallocZone*>(zone); - if (!IsMallocZoneAlreadyStored(chrome_zone)) - return; - MallocZoneFunctions& functions = GetFunctionsForZone(zone); - ReplaceZoneFunctions(chrome_zone, &functions); -} - -} // namespace - -bool UncheckedMallocMac(size_t size, void** result) { -#if defined(ADDRESS_SANITIZER) - *result = malloc(size); -#else - if (g_old_zone.malloc) { - *result = g_old_zone.malloc(malloc_default_zone(), size); - } else { - *result = malloc(size); - } -#endif // defined(ADDRESS_SANITIZER) - - return *result != NULL; -} - -bool UncheckedCallocMac(size_t num_items, size_t size, void** result) { -#if defined(ADDRESS_SANITIZER) - *result = calloc(num_items, size); -#else - if (g_old_zone.calloc) { - *result = g_old_zone.calloc(malloc_default_zone(), num_items, size); - } else { - *result = calloc(num_items, size); - } -#endif // defined(ADDRESS_SANITIZER) - - return *result != NULL; -} - -void InitializeDefaultDispatchToMacAllocator() { - StoreFunctionsForAllZones(); -} - -void StoreFunctionsForDefaultZone() { - ChromeMallocZone* default_zone = - reinterpret_cast<ChromeMallocZone*>(malloc_default_zone()); - StoreMallocZone(default_zone); -} - -void StoreFunctionsForAllZones() { - // This ensures that the default zone is always at the front of the array, - // which is important for performance. - StoreFunctionsForDefaultZone(); - - vm_address_t* zones; - unsigned int count; - kern_return_t kr = malloc_get_all_zones(mach_task_self(), 0, &zones, &count); - if (kr != KERN_SUCCESS) - return; - for (unsigned int i = 0; i < count; ++i) { - ChromeMallocZone* zone = reinterpret_cast<ChromeMallocZone*>(zones[i]); - StoreMallocZone(zone); - } -} - -void ReplaceFunctionsForStoredZones(const MallocZoneFunctions* functions) { - // The default zone does not get returned in malloc_get_all_zones(). - ChromeMallocZone* default_zone = - reinterpret_cast<ChromeMallocZone*>(malloc_default_zone()); - if (DoesMallocZoneNeedReplacing(default_zone, functions)) { - ReplaceZoneFunctions(default_zone, functions); - } - - vm_address_t* zones; - unsigned int count; - kern_return_t kr = - malloc_get_all_zones(mach_task_self(), nullptr, &zones, &count); - if (kr != KERN_SUCCESS) - return; - for (unsigned int i = 0; i < count; ++i) { - ChromeMallocZone* zone = reinterpret_cast<ChromeMallocZone*>(zones[i]); - if (DoesMallocZoneNeedReplacing(zone, functions)) { - ReplaceZoneFunctions(zone, functions); - } - } - g_replaced_default_zone = true; -} - -void InterceptAllocationsMac() { - if (g_oom_killer_enabled) - return; - - g_oom_killer_enabled = true; - - // === C malloc/calloc/valloc/realloc/posix_memalign === - - // This approach is not perfect, as requests for amounts of memory larger than - // MALLOC_ABSOLUTE_MAX_SIZE (currently SIZE_T_MAX - (2 * PAGE_SIZE)) will - // still fail with a NULL rather than dying (see malloc_zone_malloc() in - // https://opensource.apple.com/source/libmalloc/libmalloc-283/src/malloc.c - // for details). Unfortunately, it's the best we can do. Also note that this - // does not affect allocations from non-default zones. - -#if !defined(ADDRESS_SANITIZER) - // Don't do anything special on OOM for the malloc zones replaced by - // AddressSanitizer, as modifying or protecting them may not work correctly. -#if !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - // The malloc zone backed by PartitionAlloc crashes by default, so there is - // no need to install the OOM killer. - ChromeMallocZone* default_zone = - reinterpret_cast<ChromeMallocZone*>(malloc_default_zone()); - if (!IsMallocZoneAlreadyStored(default_zone)) { - StoreZoneFunctions(default_zone, &g_old_zone); - MallocZoneFunctions new_functions = {}; - new_functions.malloc = oom_killer_malloc; - new_functions.calloc = oom_killer_calloc; - new_functions.valloc = oom_killer_valloc; - new_functions.free = oom_killer_free; - new_functions.realloc = oom_killer_realloc; - new_functions.memalign = oom_killer_memalign; - - ReplaceZoneFunctions(default_zone, &new_functions); - g_replaced_default_zone = true; - } -#endif // !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - - ChromeMallocZone* purgeable_zone = - reinterpret_cast<ChromeMallocZone*>(malloc_default_purgeable_zone()); - if (purgeable_zone && !IsMallocZoneAlreadyStored(purgeable_zone)) { - StoreZoneFunctions(purgeable_zone, &g_old_purgeable_zone); - MallocZoneFunctions new_functions = {}; - new_functions.malloc = oom_killer_malloc_purgeable; - new_functions.calloc = oom_killer_calloc_purgeable; - new_functions.valloc = oom_killer_valloc_purgeable; - new_functions.free = oom_killer_free_purgeable; - new_functions.realloc = oom_killer_realloc_purgeable; - new_functions.memalign = oom_killer_memalign_purgeable; - ReplaceZoneFunctions(purgeable_zone, &new_functions); - } -#endif - - // === C malloc_zone_batch_malloc === - - // batch_malloc is omitted because the default malloc zone's implementation - // only supports batch_malloc for "tiny" allocations from the free list. It - // will fail for allocations larger than "tiny", and will only allocate as - // many blocks as it's able to from the free list. These factors mean that it - // can return less than the requested memory even in a non-out-of-memory - // situation. There's no good way to detect whether a batch_malloc failure is - // due to these other factors, or due to genuine memory or address space - // exhaustion. The fact that it only allocates space from the "tiny" free list - // means that it's likely that a failure will not be due to memory exhaustion. - // Similarly, these constraints on batch_malloc mean that callers must always - // be expecting to receive less memory than was requested, even in situations - // where memory pressure is not a concern. Finally, the only public interface - // to batch_malloc is malloc_zone_batch_malloc, which is specific to the - // system's malloc implementation. It's unlikely that anyone's even heard of - // it. - -#ifndef ADDRESS_SANITIZER - // === Core Foundation CFAllocators === - - // This will not catch allocation done by custom allocators, but will catch - // all allocation done by system-provided ones. - - CHECK(!g_old_cfallocator_system_default && !g_old_cfallocator_malloc && - !g_old_cfallocator_malloc_zone) - << "Old allocators unexpectedly non-null"; - - bool cf_allocator_internals_known = CanGetContextForCFAllocator(); - - if (cf_allocator_internals_known) { - CFAllocatorContext* context = - ContextForCFAllocator(kCFAllocatorSystemDefault); - CHECK(context) << "Failed to get context for kCFAllocatorSystemDefault."; - g_old_cfallocator_system_default = context->allocate; - CHECK(g_old_cfallocator_system_default) - << "Failed to get kCFAllocatorSystemDefault allocation function."; - context->allocate = oom_killer_cfallocator_system_default; - - context = ContextForCFAllocator(kCFAllocatorMalloc); - CHECK(context) << "Failed to get context for kCFAllocatorMalloc."; - g_old_cfallocator_malloc = context->allocate; - CHECK(g_old_cfallocator_malloc) - << "Failed to get kCFAllocatorMalloc allocation function."; - context->allocate = oom_killer_cfallocator_malloc; - - context = ContextForCFAllocator(kCFAllocatorMallocZone); - CHECK(context) << "Failed to get context for kCFAllocatorMallocZone."; - g_old_cfallocator_malloc_zone = context->allocate; - CHECK(g_old_cfallocator_malloc_zone) - << "Failed to get kCFAllocatorMallocZone allocation function."; - context->allocate = oom_killer_cfallocator_malloc_zone; - } else { - DLOG(WARNING) << "Internals of CFAllocator not known; out-of-memory " - "failures via CFAllocator will not result in termination. " - "http://crbug.com/45650"; - } -#endif - - // === Cocoa NSObject allocation === - - // Note that both +[NSObject new] and +[NSObject alloc] call through to - // +[NSObject allocWithZone:]. - - CHECK(!g_old_allocWithZone) << "Old allocator unexpectedly non-null"; - - Class nsobject_class = [NSObject class]; - Method orig_method = - class_getClassMethod(nsobject_class, @selector(allocWithZone:)); - g_old_allocWithZone = - reinterpret_cast<allocWithZone_t>(method_getImplementation(orig_method)); - CHECK(g_old_allocWithZone) - << "Failed to get allocWithZone allocation function."; - method_setImplementation(orig_method, - reinterpret_cast<IMP>(oom_killer_allocWithZone)); -} - -void UninterceptMallocZonesForTesting() { - UninterceptMallocZoneForTesting(malloc_default_zone()); // IN-TEST - vm_address_t* zones; - unsigned int count; - kern_return_t kr = malloc_get_all_zones(mach_task_self(), 0, &zones, &count); - CHECK(kr == KERN_SUCCESS); - for (unsigned int i = 0; i < count; ++i) { - UninterceptMallocZoneForTesting( // IN-TEST - reinterpret_cast<struct _malloc_zone_t*>(zones[i])); - } - - ClearAllMallocZonesForTesting(); // IN-TEST -} - -bool AreMallocZonesIntercepted() { - return !g_allocator_shims_failed_to_install; -} - -namespace { - -void ShimNewMallocZonesAndReschedule(base::Time end_time, - base::TimeDelta delay) { - ShimNewMallocZones(); - - if (base::Time::Now() > end_time) - return; - - base::TimeDelta next_delay = delay * 2; - base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask( - FROM_HERE, - base::BindOnce(&ShimNewMallocZonesAndReschedule, end_time, next_delay), - delay); -} - -} // namespace - -void PeriodicallyShimNewMallocZones() { - base::Time end_time = base::Time::Now() + base::Minutes(1); - base::TimeDelta initial_delay = base::Seconds(1); - ShimNewMallocZonesAndReschedule(end_time, initial_delay); -} - -void ShimNewMallocZones() { - StoreFunctionsForAllZones(); - - // Use the functions for the default zone as a template to replace those - // new zones. - ChromeMallocZone* default_zone = - reinterpret_cast<ChromeMallocZone*>(malloc_default_zone()); - DCHECK(IsMallocZoneAlreadyStored(default_zone)); - - MallocZoneFunctions new_functions; - StoreZoneFunctions(default_zone, &new_functions); - ReplaceFunctionsForStoredZones(&new_functions); -} - -void ReplaceZoneFunctions(ChromeMallocZone* zone, - const MallocZoneFunctions* functions) { - // Remove protection. - vm_address_t reprotection_start = 0; - vm_size_t reprotection_length = 0; - vm_prot_t reprotection_value = VM_PROT_NONE; - bool success = DeprotectMallocZone(zone, &reprotection_start, - &reprotection_length, &reprotection_value); - if (!success) { - g_allocator_shims_failed_to_install = true; - return; - } - - CHECK(functions->malloc && functions->calloc && functions->valloc && - functions->free && functions->realloc); - zone->malloc = functions->malloc; - zone->calloc = functions->calloc; - zone->valloc = functions->valloc; - zone->free = functions->free; - zone->realloc = functions->realloc; - if (functions->batch_malloc) - zone->batch_malloc = functions->batch_malloc; - if (functions->batch_free) - zone->batch_free = functions->batch_free; - if (functions->size) - zone->size = functions->size; - if (zone->version >= 5 && functions->memalign) { - zone->memalign = functions->memalign; - } - if (zone->version >= 6 && functions->free_definite_size) { - zone->free_definite_size = functions->free_definite_size; - } - if (zone->version >= 10 && functions->claimed_address) { - zone->claimed_address = functions->claimed_address; - } - if (zone->version >= 13 && functions->try_free_default) { - zone->try_free_default = functions->try_free_default; - } - - // Cap the version to the max supported to ensure malloc doesn't try to call - // functions that weren't replaced. -#if (__MAC_OS_X_VERSION_MAX_ALLOWED >= 130000) || \ - (__IPHONE_OS_VERSION_MAX_ALLOWED >= 160100) - zone->version = std::min(zone->version, 13U); -#else - zone->version = std::min(zone->version, 12U); -#endif - - // Restore protection if it was active. - if (reprotection_start) { - kern_return_t result = - vm_protect(mach_task_self(), reprotection_start, reprotection_length, - false, reprotection_value); - MACH_DCHECK(result == KERN_SUCCESS, result) << "vm_protect"; - } -} - -} // namespace allocator_shim
diff --git a/base/allocator/partition_allocator/shim/allocator_interception_mac_unittest.mm b/base/allocator/partition_allocator/shim/allocator_interception_mac_unittest.mm deleted file mode 100644 index 45a1b44..0000000 --- a/base/allocator/partition_allocator/shim/allocator_interception_mac_unittest.mm +++ /dev/null
@@ -1,63 +0,0 @@ -// Copyright 2017 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/shim/allocator_interception_mac.h" - -#include <mach/mach.h> - -#include "base/allocator/partition_allocator/shim/allocator_shim.h" -#include "base/allocator/partition_allocator/shim/malloc_zone_functions_mac.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace allocator_shim { - -namespace { -void ResetMallocZone(ChromeMallocZone* zone) { - MallocZoneFunctions& functions = GetFunctionsForZone(zone); - ReplaceZoneFunctions(zone, &functions); -} - -void ResetAllMallocZones() { - ChromeMallocZone* default_malloc_zone = - reinterpret_cast<ChromeMallocZone*>(malloc_default_zone()); - ResetMallocZone(default_malloc_zone); - - vm_address_t* zones; - unsigned int count; - kern_return_t kr = malloc_get_all_zones(mach_task_self(), 0, &zones, &count); - if (kr != KERN_SUCCESS) - return; - for (unsigned int i = 0; i < count; ++i) { - ChromeMallocZone* zone = reinterpret_cast<ChromeMallocZone*>(zones[i]); - ResetMallocZone(zone); - } -} -} // namespace - -class AllocatorInterceptionTest : public testing::Test { - protected: - void TearDown() override { - ResetAllMallocZones(); - ClearAllMallocZonesForTesting(); - } -}; - -#if !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) -TEST_F(AllocatorInterceptionTest, ShimNewMallocZones) { - InitializeAllocatorShim(); - ChromeMallocZone* default_malloc_zone = - reinterpret_cast<ChromeMallocZone*>(malloc_default_zone()); - - malloc_zone_t new_zone; - memset(&new_zone, 1, sizeof(malloc_zone_t)); - malloc_zone_register(&new_zone); - EXPECT_NE(new_zone.malloc, default_malloc_zone->malloc); - ShimNewMallocZones(); - EXPECT_EQ(new_zone.malloc, default_malloc_zone->malloc); - - malloc_zone_unregister(&new_zone); -} -#endif - -} // namespace allocator_shim
diff --git a/base/allocator/partition_allocator/shim/allocator_shim.cc b/base/allocator/partition_allocator/shim/allocator_shim.cc deleted file mode 100644 index d0e23fd..0000000 --- a/base/allocator/partition_allocator/shim/allocator_shim.cc +++ /dev/null
@@ -1,459 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/shim/allocator_shim.h" - -#include <errno.h> - -#include <atomic> -#include <new> - -#include "base/allocator/partition_allocator/partition_alloc_base/bits.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/partition_alloc_notreached.h" -#include "base/memory/page_size.h" -#include "base/threading/platform_thread.h" -#include "build/build_config.h" - -#if !BUILDFLAG(IS_WIN) -#include <unistd.h> -#else -#include "base/allocator/partition_allocator/shim/winheap_stubs_win.h" -#endif - -#if BUILDFLAG(IS_APPLE) -#include <malloc/malloc.h> - -#include "base/allocator/partition_allocator/shim/allocator_interception_mac.h" -#include "base/mac/mach_logging.h" -#endif - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -#include "base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_partition_alloc.h" -#endif - -// No calls to malloc / new in this file. They would would cause re-entrancy of -// the shim, which is hard to deal with. Keep this code as simple as possible -// and don't use any external C++ object here, not even //base ones. Even if -// they are safe to use today, in future they might be refactored. - -namespace { - -std::atomic<const allocator_shim::AllocatorDispatch*> g_chain_head{ - &allocator_shim::AllocatorDispatch::default_dispatch}; - -bool g_call_new_handler_on_malloc_failure = false; - -ALWAYS_INLINE size_t GetCachedPageSize() { - static size_t pagesize = 0; - if (!pagesize) - pagesize = base::GetPageSize(); - return pagesize; -} - -// Calls the std::new handler thread-safely. Returns true if a new_handler was -// set and called, false if no new_handler was set. -bool CallNewHandler(size_t size) { -#if BUILDFLAG(IS_WIN) - return allocator_shim::WinCallNewHandler(size); -#else - std::new_handler nh = std::get_new_handler(); - if (!nh) - return false; - (*nh)(); - // Assume the new_handler will abort if it fails. Exception are disabled and - // we don't support the case of a new_handler throwing std::bad_balloc. - return true; -#endif -} - -ALWAYS_INLINE const allocator_shim::AllocatorDispatch* GetChainHead() { - return g_chain_head.load(std::memory_order_relaxed); -} - -} // namespace - -namespace allocator_shim { - -void SetCallNewHandlerOnMallocFailure(bool value) { - g_call_new_handler_on_malloc_failure = value; - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - allocator_shim::internal::PartitionAllocSetCallNewHandlerOnMallocFailure( - value); -#endif -} - -void* UncheckedAlloc(size_t size) { - const AllocatorDispatch* const chain_head = GetChainHead(); - return chain_head->alloc_unchecked_function(chain_head, size, nullptr); -} - -void UncheckedFree(void* ptr) { - const AllocatorDispatch* const chain_head = GetChainHead(); - return chain_head->free_function(chain_head, ptr, nullptr); -} - -void InsertAllocatorDispatch(AllocatorDispatch* dispatch) { - // Loop in case of (an unlikely) race on setting the list head. - size_t kMaxRetries = 7; - for (size_t i = 0; i < kMaxRetries; ++i) { - const AllocatorDispatch* chain_head = GetChainHead(); - dispatch->next = chain_head; - - // This function guarantees to be thread-safe w.r.t. concurrent - // insertions. It also has to guarantee that all the threads always - // see a consistent chain, hence the atomic_thread_fence() below. - // InsertAllocatorDispatch() is NOT a fastpath, as opposite to malloc(), so - // we don't really want this to be a release-store with a corresponding - // acquire-load during malloc(). - std::atomic_thread_fence(std::memory_order_seq_cst); - // Set the chain head to the new dispatch atomically. If we lose the race, - // retry. - if (g_chain_head.compare_exchange_strong(chain_head, dispatch, - std::memory_order_relaxed, - std::memory_order_relaxed)) { - // Success. - return; - } - } - - PA_CHECK(false); // Too many retries, this shouldn't happen. -} - -void RemoveAllocatorDispatchForTesting(AllocatorDispatch* dispatch) { - PA_DCHECK(GetChainHead() == dispatch); - g_chain_head.store(dispatch->next, std::memory_order_relaxed); -} - -#if BUILDFLAG(IS_APPLE) -void TryFreeDefaultFallbackToFindZoneAndFree(void* ptr) { - unsigned int zone_count = 0; - vm_address_t* zones = nullptr; - kern_return_t result = - malloc_get_all_zones(mach_task_self(), nullptr, &zones, &zone_count); - MACH_CHECK(result == KERN_SUCCESS, result) << "malloc_get_all_zones"; - - // "find_zone_and_free" expected by try_free_default. - // - // libmalloc's zones call find_registered_zone() in case the default one - // doesn't handle the allocation. We can't, so we try to emulate it. See the - // implementation in libmalloc/src/malloc.c for details. - // https://github.com/apple-oss-distributions/libmalloc/blob/main/src/malloc.c - for (unsigned int i = 0; i < zone_count; ++i) { - malloc_zone_t* zone = reinterpret_cast<malloc_zone_t*>(zones[i]); - if (size_t size = zone->size(zone, ptr)) { - if (zone->version >= 6 && zone->free_definite_size) { - zone->free_definite_size(zone, ptr, size); - } else { - zone->free(zone, ptr); - } - return; - } - } - - // There must be an owner zone. - PA_CHECK(false); -} -#endif // BUILDFLAG(IS_APPLE) - -} // namespace allocator_shim - -// The Shim* functions below are the entry-points into the shim-layer and -// are supposed to be invoked by the allocator_shim_override_* -// headers to route the malloc / new symbols through the shim layer. -// They are defined as ALWAYS_INLINE in order to remove a level of indirection -// between the system-defined entry points and the shim implementations. -extern "C" { - -// The general pattern for allocations is: -// - Try to allocate, if succeded return the pointer. -// - If the allocation failed: -// - Call the std::new_handler if it was a C++ allocation. -// - Call the std::new_handler if it was a malloc() (or calloc() or similar) -// AND SetCallNewHandlerOnMallocFailure(true). -// - If the std::new_handler is NOT set just return nullptr. -// - If the std::new_handler is set: -// - Assume it will abort() if it fails (very likely the new_handler will -// just suicide printing a message). -// - Assume it did succeed if it returns, in which case reattempt the alloc. - -ALWAYS_INLINE void* ShimCppNew(size_t size) { - const allocator_shim::AllocatorDispatch* const chain_head = GetChainHead(); - void* ptr; - do { - void* context = nullptr; -#if BUILDFLAG(IS_APPLE) && !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - context = malloc_default_zone(); -#endif - ptr = chain_head->alloc_function(chain_head, size, context); - } while (!ptr && CallNewHandler(size)); - return ptr; -} - -ALWAYS_INLINE void* ShimCppNewNoThrow(size_t size) { - void* context = nullptr; -#if BUILDFLAG(IS_APPLE) && !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - context = malloc_default_zone(); -#endif - const allocator_shim::AllocatorDispatch* const chain_head = GetChainHead(); - return chain_head->alloc_unchecked_function(chain_head, size, context); -} - -ALWAYS_INLINE void* ShimCppAlignedNew(size_t size, size_t alignment) { - const allocator_shim::AllocatorDispatch* const chain_head = GetChainHead(); - void* ptr; - do { - void* context = nullptr; -#if BUILDFLAG(IS_APPLE) && !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - context = malloc_default_zone(); -#endif - ptr = chain_head->alloc_aligned_function(chain_head, alignment, size, - context); - } while (!ptr && CallNewHandler(size)); - return ptr; -} - -ALWAYS_INLINE void ShimCppDelete(void* address) { - void* context = nullptr; -#if BUILDFLAG(IS_APPLE) && !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - context = malloc_default_zone(); -#endif - const allocator_shim::AllocatorDispatch* const chain_head = GetChainHead(); - return chain_head->free_function(chain_head, address, context); -} - -ALWAYS_INLINE void* ShimMalloc(size_t size, void* context) { - const allocator_shim::AllocatorDispatch* const chain_head = GetChainHead(); - void* ptr; - do { - ptr = chain_head->alloc_function(chain_head, size, context); - } while (!ptr && g_call_new_handler_on_malloc_failure && - CallNewHandler(size)); - return ptr; -} - -ALWAYS_INLINE void* ShimCalloc(size_t n, size_t size, void* context) { - const allocator_shim::AllocatorDispatch* const chain_head = GetChainHead(); - void* ptr; - do { - ptr = chain_head->alloc_zero_initialized_function(chain_head, n, size, - context); - } while (!ptr && g_call_new_handler_on_malloc_failure && - CallNewHandler(size)); - return ptr; -} - -ALWAYS_INLINE void* ShimRealloc(void* address, size_t size, void* context) { - // realloc(size == 0) means free() and might return a nullptr. We should - // not call the std::new_handler in that case, though. - const allocator_shim::AllocatorDispatch* const chain_head = GetChainHead(); - void* ptr; - do { - ptr = chain_head->realloc_function(chain_head, address, size, context); - } while (!ptr && size && g_call_new_handler_on_malloc_failure && - CallNewHandler(size)); - return ptr; -} - -ALWAYS_INLINE void* ShimMemalign(size_t alignment, size_t size, void* context) { - const allocator_shim::AllocatorDispatch* const chain_head = GetChainHead(); - void* ptr; - do { - ptr = chain_head->alloc_aligned_function(chain_head, alignment, size, - context); - } while (!ptr && g_call_new_handler_on_malloc_failure && - CallNewHandler(size)); - return ptr; -} - -ALWAYS_INLINE int ShimPosixMemalign(void** res, size_t alignment, size_t size) { - // posix_memalign is supposed to check the arguments. See tc_posix_memalign() - // in tc_malloc.cc. - if (((alignment % sizeof(void*)) != 0) || - !partition_alloc::internal::base::bits::IsPowerOfTwo(alignment)) { - return EINVAL; - } - void* ptr = ShimMemalign(alignment, size, nullptr); - *res = ptr; - return ptr ? 0 : ENOMEM; -} - -ALWAYS_INLINE void* ShimValloc(size_t size, void* context) { - return ShimMemalign(GetCachedPageSize(), size, context); -} - -ALWAYS_INLINE void* ShimPvalloc(size_t size) { - // pvalloc(0) should allocate one page, according to its man page. - if (size == 0) { - size = GetCachedPageSize(); - } else { - size = partition_alloc::internal::base::bits::AlignUp(size, - GetCachedPageSize()); - } - // The third argument is nullptr because pvalloc is glibc only and does not - // exist on OSX/BSD systems. - return ShimMemalign(GetCachedPageSize(), size, nullptr); -} - -ALWAYS_INLINE void ShimFree(void* address, void* context) { - const allocator_shim::AllocatorDispatch* const chain_head = GetChainHead(); - return chain_head->free_function(chain_head, address, context); -} - -ALWAYS_INLINE size_t ShimGetSizeEstimate(const void* address, void* context) { - const allocator_shim::AllocatorDispatch* const chain_head = GetChainHead(); - return chain_head->get_size_estimate_function( - chain_head, const_cast<void*>(address), context); -} - -ALWAYS_INLINE bool ShimClaimedAddress(void* address, void* context) { - const allocator_shim::AllocatorDispatch* const chain_head = GetChainHead(); - return chain_head->claimed_address_function(chain_head, address, context); -} - -ALWAYS_INLINE unsigned ShimBatchMalloc(size_t size, - void** results, - unsigned num_requested, - void* context) { - const allocator_shim::AllocatorDispatch* const chain_head = GetChainHead(); - return chain_head->batch_malloc_function(chain_head, size, results, - num_requested, context); -} - -ALWAYS_INLINE void ShimBatchFree(void** to_be_freed, - unsigned num_to_be_freed, - void* context) { - const allocator_shim::AllocatorDispatch* const chain_head = GetChainHead(); - return chain_head->batch_free_function(chain_head, to_be_freed, - num_to_be_freed, context); -} - -ALWAYS_INLINE void ShimFreeDefiniteSize(void* ptr, size_t size, void* context) { - const allocator_shim::AllocatorDispatch* const chain_head = GetChainHead(); - return chain_head->free_definite_size_function(chain_head, ptr, size, - context); -} - -ALWAYS_INLINE void ShimTryFreeDefault(void* ptr, void* context) { - const allocator_shim::AllocatorDispatch* const chain_head = GetChainHead(); - return chain_head->try_free_default_function(chain_head, ptr, context); -} - -ALWAYS_INLINE void* ShimAlignedMalloc(size_t size, - size_t alignment, - void* context) { - const allocator_shim::AllocatorDispatch* const chain_head = GetChainHead(); - void* ptr = nullptr; - do { - ptr = chain_head->aligned_malloc_function(chain_head, size, alignment, - context); - } while (!ptr && g_call_new_handler_on_malloc_failure && - CallNewHandler(size)); - return ptr; -} - -ALWAYS_INLINE void* ShimAlignedRealloc(void* address, - size_t size, - size_t alignment, - void* context) { - // _aligned_realloc(size == 0) means _aligned_free() and might return a - // nullptr. We should not call the std::new_handler in that case, though. - const allocator_shim::AllocatorDispatch* const chain_head = GetChainHead(); - void* ptr = nullptr; - do { - ptr = chain_head->aligned_realloc_function(chain_head, address, size, - alignment, context); - } while (!ptr && size && g_call_new_handler_on_malloc_failure && - CallNewHandler(size)); - return ptr; -} - -ALWAYS_INLINE void ShimAlignedFree(void* address, void* context) { - const allocator_shim::AllocatorDispatch* const chain_head = GetChainHead(); - return chain_head->aligned_free_function(chain_head, address, context); -} - -} // extern "C" - -#if !BUILDFLAG(IS_WIN) && \ - !(BUILDFLAG(IS_APPLE) && !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)) -// Cpp symbols (new / delete) should always be routed through the shim layer -// except on Windows and macOS (except for PartitionAlloc-Everywhere) where the -// malloc intercept is deep enough that it also catches the cpp calls. -// -// In case of PartitionAlloc-Everywhere on macOS, malloc backed by -// allocator_shim::internal::PartitionMalloc crashes on OOM, and we need to -// avoid crashes in case of operator new() noexcept. Thus, operator new() -// noexcept needs to be routed to -// allocator_shim::internal::PartitionMallocUnchecked through the shim layer. -#include "base/allocator/partition_allocator/shim/allocator_shim_override_cpp_symbols.h" -#endif - -#if BUILDFLAG(IS_ANDROID) -// Android does not support symbol interposition. The way malloc symbols are -// intercepted on Android is by using link-time -wrap flags. -#include "base/allocator/partition_allocator/shim/allocator_shim_override_linker_wrapped_symbols.h" -#elif BUILDFLAG(IS_WIN) -// On Windows we use plain link-time overriding of the CRT symbols. -#include "base/allocator/partition_allocator/shim/allocator_shim_override_ucrt_symbols_win.h" -#elif BUILDFLAG(IS_APPLE) -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -#include "base/allocator/partition_allocator/shim/allocator_shim_override_mac_default_zone.h" -#else // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -#include "base/allocator/partition_allocator/shim/allocator_shim_override_mac_symbols.h" -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -#else -#include "base/allocator/partition_allocator/shim/allocator_shim_override_libc_symbols.h" -#endif - -// Some glibc versions (until commit 6c444ad6e953dbdf9c7be065308a0a777) -// incorrectly call __libc_memalign() to allocate memory (see elf/dl-tls.c in -// glibc 2.23 for instance), and free() to free it. This causes issues for us, -// as we are then asked to free memory we didn't allocate. -// -// This only happened in glibc to allocate TLS storage metadata, and there are -// no other callers of __libc_memalign() there as of September 2020. To work -// around this issue, intercept this internal libc symbol to make sure that both -// the allocation and the free() are caught by the shim. -// -// This seems fragile, and is, but there is ample precedent for it, making it -// quite likely to keep working in the future. For instance, LLVM for LSAN uses -// this mechanism. - -#if defined(LIBC_GLIBC) && BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -#include "base/allocator/partition_allocator/shim/allocator_shim_override_glibc_weak_symbols.h" -#endif - -#if BUILDFLAG(IS_APPLE) -namespace allocator_shim { - -void InitializeAllocatorShim() { -#if !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - // Prepares the default dispatch. After the intercepted malloc calls have - // traversed the shim this will route them to the default malloc zone. - InitializeDefaultDispatchToMacAllocator(); - - MallocZoneFunctions functions = MallocZoneFunctionsToReplaceDefault(); - - // This replaces the default malloc zone, causing calls to malloc & friends - // from the codebase to be routed to ShimMalloc() above. - ReplaceFunctionsForStoredZones(&functions); -#endif // !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -} - -} // namespace allocator_shim -#endif - -// Cross-checks. - -#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR) -#error The allocator shim should not be compiled when building for memory tools. -#endif - -#if (defined(__GNUC__) && defined(__EXCEPTIONS)) || \ - (defined(_MSC_VER) && defined(_CPPUNWIND)) -#error This code cannot be used when exceptions are turned on. -#endif
diff --git a/base/allocator/partition_allocator/shim/allocator_shim.h b/base/allocator/partition_allocator/shim/allocator_shim.h deleted file mode 100644 index 6d946c7..0000000 --- a/base/allocator/partition_allocator/shim/allocator_shim.h +++ /dev/null
@@ -1,212 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_H_ - -#include <stddef.h> -#include <stdint.h> - -#include "base/allocator/partition_alloc_features.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/base_export.h" -#include "base/types/strong_alias.h" -#include "build/build_config.h" - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && BUILDFLAG(USE_STARSCAN) -#include "base/allocator/partition_allocator/starscan/pcscan.h" -#endif - -namespace allocator_shim { - -// Allocator Shim API. Allows to: -// - Configure the behavior of the allocator (what to do on OOM failures). -// - Install new hooks (AllocatorDispatch) in the allocator chain. - -// When this shim layer is enabled, the route of an allocation is as-follows: -// -// [allocator_shim_override_*.h] Intercept malloc() / operator new calls: -// The override_* headers define the symbols required to intercept calls to -// malloc() and operator new (if not overridden by specific C++ classes). -// -// [allocator_shim.cc] Routing allocation calls to the shim: -// The headers above route the calls to the internal ShimMalloc(), ShimFree(), -// ShimCppNew() etc. methods defined in allocator_shim.cc. -// These methods will: (1) forward the allocation call to the front of the -// AllocatorDispatch chain. (2) perform security hardenings (e.g., might -// call std::new_handler on OOM failure). -// -// [allocator_shim_default_dispatch_to_*.cc] The AllocatorDispatch chain: -// It is a singly linked list where each element is a struct with function -// pointers (|malloc_function|, |free_function|, etc). Normally the chain -// consists of a single AllocatorDispatch element, herein called -// the "default dispatch", which is statically defined at build time and -// ultimately routes the calls to the actual allocator defined by the build -// config (glibc, ...). -// -// It is possible to dynamically insert further AllocatorDispatch stages -// to the front of the chain, for debugging / profiling purposes. -// -// All the functions must be thread safe. The shim does not enforce any -// serialization. This is to route to thread-aware allocators without -// introducing unnecessary perf hits. - -struct AllocatorDispatch { - using AllocFn = void*(const AllocatorDispatch* self, - size_t size, - void* context); - using AllocUncheckedFn = void*(const AllocatorDispatch* self, - size_t size, - void* context); - using AllocZeroInitializedFn = void*(const AllocatorDispatch* self, - size_t n, - size_t size, - void* context); - using AllocAlignedFn = void*(const AllocatorDispatch* self, - size_t alignment, - size_t size, - void* context); - using ReallocFn = void*(const AllocatorDispatch* self, - void* address, - size_t size, - void* context); - using FreeFn = void(const AllocatorDispatch* self, - void* address, - void* context); - // Returns the allocated size of user data (not including heap overhead). - // Can be larger than the requested size. - using GetSizeEstimateFn = size_t(const AllocatorDispatch* self, - void* address, - void* context); - using ClaimedAddressFn = bool(const AllocatorDispatch* self, - void* address, - void* context); - using BatchMallocFn = unsigned(const AllocatorDispatch* self, - size_t size, - void** results, - unsigned num_requested, - void* context); - using BatchFreeFn = void(const AllocatorDispatch* self, - void** to_be_freed, - unsigned num_to_be_freed, - void* context); - using FreeDefiniteSizeFn = void(const AllocatorDispatch* self, - void* ptr, - size_t size, - void* context); - using TryFreeDefaultFn = void(const AllocatorDispatch* self, - void* ptr, - void* context); - using AlignedMallocFn = void*(const AllocatorDispatch* self, - size_t size, - size_t alignment, - void* context); - using AlignedReallocFn = void*(const AllocatorDispatch* self, - void* address, - size_t size, - size_t alignment, - void* context); - using AlignedFreeFn = void(const AllocatorDispatch* self, - void* address, - void* context); - - AllocFn* const alloc_function; - AllocUncheckedFn* const alloc_unchecked_function; - AllocZeroInitializedFn* const alloc_zero_initialized_function; - AllocAlignedFn* const alloc_aligned_function; - ReallocFn* const realloc_function; - FreeFn* const free_function; - GetSizeEstimateFn* const get_size_estimate_function; - // claimed_address, batch_malloc, batch_free, free_definite_size and - // try_free_default are specific to the OSX and iOS allocators. - ClaimedAddressFn* const claimed_address_function; - BatchMallocFn* const batch_malloc_function; - BatchFreeFn* const batch_free_function; - FreeDefiniteSizeFn* const free_definite_size_function; - TryFreeDefaultFn* const try_free_default_function; - // _aligned_malloc, _aligned_realloc, and _aligned_free are specific to the - // Windows allocator. - AlignedMallocFn* const aligned_malloc_function; - AlignedReallocFn* const aligned_realloc_function; - AlignedFreeFn* const aligned_free_function; - - const AllocatorDispatch* next; - - // |default_dispatch| is statically defined by one (and only one) of the - // allocator_shim_default_dispatch_to_*.cc files, depending on the build - // configuration. - static const AllocatorDispatch default_dispatch; -}; - -// When true makes malloc behave like new, w.r.t calling the new_handler if -// the allocation fails (see set_new_mode() in Windows). -BASE_EXPORT void SetCallNewHandlerOnMallocFailure(bool value); - -// Allocates |size| bytes or returns nullptr. It does NOT call the new_handler, -// regardless of SetCallNewHandlerOnMallocFailure(). -BASE_EXPORT void* UncheckedAlloc(size_t size); - -// Frees memory allocated with UncheckedAlloc(). -BASE_EXPORT void UncheckedFree(void* ptr); - -// Inserts |dispatch| in front of the allocator chain. This method is -// thread-safe w.r.t concurrent invocations of InsertAllocatorDispatch(). -// The callers have responsibility for inserting a single dispatch no more -// than once. -BASE_EXPORT void InsertAllocatorDispatch(AllocatorDispatch* dispatch); - -// Test-only. Rationale: (1) lack of use cases; (2) dealing safely with a -// removal of arbitrary elements from a singly linked list would require a lock -// in malloc(), which we really don't want. -BASE_EXPORT void RemoveAllocatorDispatchForTesting(AllocatorDispatch* dispatch); - -#if BUILDFLAG(IS_APPLE) -// The fallback function to be called when try_free_default_function receives a -// pointer which doesn't belong to the allocator. -BASE_EXPORT void TryFreeDefaultFallbackToFindZoneAndFree(void* ptr); -#endif // BUILDFLAG(IS_APPLE) - -#if BUILDFLAG(IS_APPLE) -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -BASE_EXPORT void InitializeDefaultAllocatorPartitionRoot(); -bool IsDefaultAllocatorPartitionRootInitialized(); -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -// On macOS, the allocator shim needs to be turned on during runtime. -BASE_EXPORT void InitializeAllocatorShim(); -#endif // BUILDFLAG(IS_APPLE) - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -BASE_EXPORT void EnablePartitionAllocMemoryReclaimer(); - -using EnableBrp = base::StrongAlias<class EnableBrpTag, bool>; -using EnableBrpZapping = base::StrongAlias<class EnableBrpZappingTag, bool>; -using EnableBrpPartitionMemoryReclaimer = - base::StrongAlias<class EnableBrpPartitionMemoryReclaimerTag, bool>; -using SplitMainPartition = base::StrongAlias<class SplitMainPartitionTag, bool>; -using UseDedicatedAlignedPartition = - base::StrongAlias<class UseDedicatedAlignedPartitionTag, bool>; -using AddDummyRefCount = base::StrongAlias<class AddDummyRefCountTag, bool>; -using AlternateBucketDistribution = - base::features::AlternateBucketDistributionMode; - -// If |thread_cache_on_non_quarantinable_partition| is specified, the -// thread-cache will be enabled on the non-quarantinable partition. The -// thread-cache on the main (malloc) partition will be disabled. -BASE_EXPORT void ConfigurePartitions( - EnableBrp enable_brp, - EnableBrpZapping enable_brp_zapping, - EnableBrpPartitionMemoryReclaimer enable_brp_memory_reclaimer, - SplitMainPartition split_main_partition, - UseDedicatedAlignedPartition use_dedicated_aligned_partition, - AddDummyRefCount add_dummy_ref_count, - AlternateBucketDistribution use_alternate_bucket_distribution); - -#if BUILDFLAG(USE_STARSCAN) -BASE_EXPORT void EnablePCScan(partition_alloc::internal::PCScan::InitConfig); -#endif -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - -} // namespace allocator_shim - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_H_
diff --git a/base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_glibc.cc b/base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_glibc.cc deleted file mode 100644 index 2c833f4..0000000 --- a/base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_glibc.cc +++ /dev/null
@@ -1,124 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <limits> - -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/numerics/checked_math.h" -#include "base/allocator/partition_allocator/shim/allocator_shim.h" -#include "base/process/memory.h" - -#include <dlfcn.h> -#include <malloc.h> - -// This translation unit defines a default dispatch for the allocator shim which -// routes allocations to libc functions. -// The code here is strongly inspired from tcmalloc's libc_override_glibc.h. - -extern "C" { -void* __libc_malloc(size_t size); -void* __libc_calloc(size_t n, size_t size); -void* __libc_realloc(void* address, size_t size); -void* __libc_memalign(size_t alignment, size_t size); -void __libc_free(void* ptr); -} // extern "C" - -namespace { - -using allocator_shim::AllocatorDispatch; - -// Strictly speaking, it would make more sense to not subtract amything, but -// other shims limit to something lower than INT_MAX (which is 0x7FFFFFFF on -// most platforms), and tests expect that. -constexpr size_t kMaxAllowedSize = std::numeric_limits<int>::max() - (1 << 12); - -void* GlibcMalloc(const AllocatorDispatch*, size_t size, void* context) { - // Cannot force glibc's malloc() to crash when a large size is requested, do - // it in the shim instead. - if (PA_UNLIKELY(size >= kMaxAllowedSize)) - base::TerminateBecauseOutOfMemory(size); - - return __libc_malloc(size); -} - -void* GlibcUncheckedMalloc(const AllocatorDispatch*, - size_t size, - void* context) { - if (PA_UNLIKELY(size >= kMaxAllowedSize)) - return nullptr; - - return __libc_malloc(size); -} - -void* GlibcCalloc(const AllocatorDispatch*, - size_t n, - size_t size, - void* context) { - const auto total = partition_alloc::internal::base::CheckMul(n, size); - if (PA_UNLIKELY(!total.IsValid() || total.ValueOrDie() >= kMaxAllowedSize)) - base::TerminateBecauseOutOfMemory(size * n); - - return __libc_calloc(n, size); -} - -void* GlibcRealloc(const AllocatorDispatch*, - void* address, - size_t size, - void* context) { - if (PA_UNLIKELY(size >= kMaxAllowedSize)) - base::TerminateBecauseOutOfMemory(size); - - return __libc_realloc(address, size); -} - -void* GlibcMemalign(const AllocatorDispatch*, - size_t alignment, - size_t size, - void* context) { - if (PA_UNLIKELY(size >= kMaxAllowedSize)) - base::TerminateBecauseOutOfMemory(size); - - return __libc_memalign(alignment, size); -} - -void GlibcFree(const AllocatorDispatch*, void* address, void* context) { - __libc_free(address); -} - -PA_NO_SANITIZE("cfi-icall") -size_t GlibcGetSizeEstimate(const AllocatorDispatch*, - void* address, - void* context) { - // glibc does not expose an alias to resolve malloc_usable_size. Dynamically - // resolve it instead. This should be safe because glibc (and hence dlfcn) - // does not use malloc_size internally and so there should not be a risk of - // recursion. - using MallocUsableSizeFunction = decltype(malloc_usable_size)*; - static MallocUsableSizeFunction fn_ptr = - reinterpret_cast<MallocUsableSizeFunction>( - dlsym(RTLD_NEXT, "malloc_usable_size")); - - return fn_ptr(address); -} - -} // namespace - -const AllocatorDispatch AllocatorDispatch::default_dispatch = { - &GlibcMalloc, /* alloc_function */ - &GlibcUncheckedMalloc, /* alloc_unchecked_function */ - &GlibcCalloc, /* alloc_zero_initialized_function */ - &GlibcMemalign, /* alloc_aligned_function */ - &GlibcRealloc, /* realloc_function */ - &GlibcFree, /* free_function */ - &GlibcGetSizeEstimate, /* get_size_estimate_function */ - nullptr, /* claimed_address */ - nullptr, /* batch_malloc_function */ - nullptr, /* batch_free_function */ - nullptr, /* free_definite_size_function */ - nullptr, /* try_free_default_function */ - nullptr, /* aligned_malloc_function */ - nullptr, /* aligned_realloc_function */ - nullptr, /* aligned_free_function */ - nullptr, /* next */ -};
diff --git a/base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_linker_wrapped_symbols.cc b/base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_linker_wrapped_symbols.cc deleted file mode 100644 index 6a73d8a..0000000 --- a/base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_linker_wrapped_symbols.cc +++ /dev/null
@@ -1,86 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <malloc.h> - -#include "base/allocator/partition_allocator/shim/allocator_shim.h" -#include "build/build_config.h" - -// This translation unit defines a default dispatch for the allocator shim which -// routes allocations to the original libc functions when using the link-time -// -Wl,-wrap,malloc approach (see README.md). -// The __real_X functions here are special symbols that the linker will relocate -// against the real "X" undefined symbol, so that __real_malloc becomes the -// equivalent of what an undefined malloc symbol reference would have been. -// This is the counterpart of allocator_shim_override_linker_wrapped_symbols.h, -// which routes the __wrap_X functions into the shim. - -extern "C" { -void* __real_malloc(size_t); -void* __real_calloc(size_t, size_t); -void* __real_realloc(void*, size_t); -void* __real_memalign(size_t, size_t); -void __real_free(void*); -size_t __real_malloc_usable_size(void*); -} // extern "C" - -namespace { - -using allocator_shim::AllocatorDispatch; - -void* RealMalloc(const AllocatorDispatch*, size_t size, void* context) { - return __real_malloc(size); -} - -void* RealCalloc(const AllocatorDispatch*, - size_t n, - size_t size, - void* context) { - return __real_calloc(n, size); -} - -void* RealRealloc(const AllocatorDispatch*, - void* address, - size_t size, - void* context) { - return __real_realloc(address, size); -} - -void* RealMemalign(const AllocatorDispatch*, - size_t alignment, - size_t size, - void* context) { - return __real_memalign(alignment, size); -} - -void RealFree(const AllocatorDispatch*, void* address, void* context) { - __real_free(address); -} - -size_t RealSizeEstimate(const AllocatorDispatch*, - void* address, - void* context) { - return __real_malloc_usable_size(address); -} - -} // namespace - -const AllocatorDispatch AllocatorDispatch::default_dispatch = { - &RealMalloc, /* alloc_function */ - &RealMalloc, /* alloc_unchecked_function */ - &RealCalloc, /* alloc_zero_initialized_function */ - &RealMemalign, /* alloc_aligned_function */ - &RealRealloc, /* realloc_function */ - &RealFree, /* free_function */ - &RealSizeEstimate, /* get_size_estimate_function */ - nullptr, /* claimed_address */ - nullptr, /* batch_malloc_function */ - nullptr, /* batch_free_function */ - nullptr, /* free_definite_size_function */ - nullptr, /* try_free_default_function */ - nullptr, /* aligned_malloc_function */ - nullptr, /* aligned_realloc_function */ - nullptr, /* aligned_free_function */ - nullptr, /* next */ -};
diff --git a/base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_mac_zoned_malloc.cc b/base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_mac_zoned_malloc.cc deleted file mode 100644 index 76b275c..0000000 --- a/base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_mac_zoned_malloc.cc +++ /dev/null
@@ -1,130 +0,0 @@ -// Copyright 2017 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <utility> - -#include "base/allocator/partition_allocator/shim/allocator_interception_mac.h" -#include "base/allocator/partition_allocator/shim/allocator_shim.h" -#include "base/allocator/partition_allocator/shim/malloc_zone_functions_mac.h" - -namespace allocator_shim { -namespace { - -void* MallocImpl(const AllocatorDispatch*, size_t size, void* context) { - MallocZoneFunctions& functions = GetFunctionsForZone(context); - return functions.malloc(reinterpret_cast<struct _malloc_zone_t*>(context), - size); -} - -void* CallocImpl(const AllocatorDispatch*, - size_t n, - size_t size, - void* context) { - MallocZoneFunctions& functions = GetFunctionsForZone(context); - return functions.calloc(reinterpret_cast<struct _malloc_zone_t*>(context), n, - size); -} - -void* MemalignImpl(const AllocatorDispatch*, - size_t alignment, - size_t size, - void* context) { - MallocZoneFunctions& functions = GetFunctionsForZone(context); - return functions.memalign(reinterpret_cast<struct _malloc_zone_t*>(context), - alignment, size); -} - -void* ReallocImpl(const AllocatorDispatch*, - void* ptr, - size_t size, - void* context) { - MallocZoneFunctions& functions = GetFunctionsForZone(context); - return functions.realloc(reinterpret_cast<struct _malloc_zone_t*>(context), - ptr, size); -} - -void FreeImpl(const AllocatorDispatch*, void* ptr, void* context) { - MallocZoneFunctions& functions = GetFunctionsForZone(context); - functions.free(reinterpret_cast<struct _malloc_zone_t*>(context), ptr); -} - -size_t GetSizeEstimateImpl(const AllocatorDispatch*, void* ptr, void* context) { - MallocZoneFunctions& functions = GetFunctionsForZone(context); - return functions.size(reinterpret_cast<struct _malloc_zone_t*>(context), ptr); -} - -bool ClaimedAddressImpl(const AllocatorDispatch*, void* ptr, void* context) { - MallocZoneFunctions& functions = GetFunctionsForZone(context); - if (functions.claimed_address) { - return functions.claimed_address( - reinterpret_cast<struct _malloc_zone_t*>(context), ptr); - } - // If the fast API 'claimed_address' is not implemented in the specified zone, - // fall back to 'size' function, which also tells whether the given address - // belongs to the zone or not although it'd be slow. - return functions.size(reinterpret_cast<struct _malloc_zone_t*>(context), ptr); -} - -unsigned BatchMallocImpl(const AllocatorDispatch* self, - size_t size, - void** results, - unsigned num_requested, - void* context) { - MallocZoneFunctions& functions = GetFunctionsForZone(context); - return functions.batch_malloc( - reinterpret_cast<struct _malloc_zone_t*>(context), size, results, - num_requested); -} - -void BatchFreeImpl(const AllocatorDispatch* self, - void** to_be_freed, - unsigned num_to_be_freed, - void* context) { - MallocZoneFunctions& functions = GetFunctionsForZone(context); - functions.batch_free(reinterpret_cast<struct _malloc_zone_t*>(context), - to_be_freed, num_to_be_freed); -} - -void FreeDefiniteSizeImpl(const AllocatorDispatch* self, - void* ptr, - size_t size, - void* context) { - MallocZoneFunctions& functions = GetFunctionsForZone(context); - functions.free_definite_size( - reinterpret_cast<struct _malloc_zone_t*>(context), ptr, size); -} - -void TryFreeDefaultImpl(const AllocatorDispatch* self, - void* ptr, - void* context) { - MallocZoneFunctions& functions = GetFunctionsForZone(context); - if (functions.try_free_default) { - return functions.try_free_default( - reinterpret_cast<struct _malloc_zone_t*>(context), ptr); - } - allocator_shim::TryFreeDefaultFallbackToFindZoneAndFree(ptr); -} - -} // namespace - -const AllocatorDispatch AllocatorDispatch::default_dispatch = { - &MallocImpl, /* alloc_function */ - &MallocImpl, /* alloc_unchecked_function */ - &CallocImpl, /* alloc_zero_initialized_function */ - &MemalignImpl, /* alloc_aligned_function */ - &ReallocImpl, /* realloc_function */ - &FreeImpl, /* free_function */ - &GetSizeEstimateImpl, /* get_size_estimate_function */ - &ClaimedAddressImpl, /* claimed_address_function */ - &BatchMallocImpl, /* batch_malloc_function */ - &BatchFreeImpl, /* batch_free_function */ - &FreeDefiniteSizeImpl, /* free_definite_size_function */ - &TryFreeDefaultImpl, /* try_free_default_function */ - nullptr, /* aligned_malloc_function */ - nullptr, /* aligned_realloc_function */ - nullptr, /* aligned_free_function */ - nullptr, /* next */ -}; - -} // namespace allocator_shim
diff --git a/base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_partition_alloc.cc b/base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_partition_alloc.cc deleted file mode 100644 index 4f7a87d..0000000 --- a/base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_partition_alloc.cc +++ /dev/null
@@ -1,820 +0,0 @@ -// 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. - -#include "base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_partition_alloc.h" - -#include <atomic> -#include <cstddef> -#include <map> -#include <string> -#include <tuple> - -#include "base/allocator/partition_alloc_features.h" -#include "base/allocator/partition_allocator/allocation_guard.h" -#include "base/allocator/partition_allocator/memory_reclaimer.h" -#include "base/allocator/partition_allocator/partition_alloc.h" -#include "base/allocator/partition_allocator/partition_alloc_base/bits.h" -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_base/no_destructor.h" -#include "base/allocator/partition_allocator/partition_alloc_base/numerics/checked_math.h" -#include "base/allocator/partition_allocator/partition_alloc_base/numerics/safe_conversions.h" -#include "base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "base/allocator/partition_allocator/partition_root.h" -#include "base/allocator/partition_allocator/partition_stats.h" -#include "base/allocator/partition_allocator/shim/allocator_shim_internals.h" -#include "base/memory/nonscannable_memory.h" -#include "base/threading/platform_thread.h" -#include "build/build_config.h" -#include "build/chromecast_buildflags.h" - -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) -#include <malloc.h> -#endif - -using allocator_shim::AllocatorDispatch; - -namespace { - -class SimpleScopedSpinLocker { - public: - explicit SimpleScopedSpinLocker(std::atomic<bool>& lock) : lock_(lock) { - // Lock. Semantically equivalent to base::Lock::Acquire(). - bool expected = false; - // Weak CAS since we are in a retry loop, relaxed ordering for failure since - // in this case we don't imply any ordering. - // - // This matches partition_allocator/spinning_mutex.h fast path on Linux. - while (!lock_.compare_exchange_weak( - expected, true, std::memory_order_acquire, std::memory_order_relaxed)) { - expected = false; - } - } - - ~SimpleScopedSpinLocker() { lock_.store(false, std::memory_order_release); } - - private: - std::atomic<bool>& lock_; -}; - -// We can't use a "static local" or a base::LazyInstance, as: -// - static local variables call into the runtime on Windows, which is not -// prepared to handle it, as the first allocation happens during CRT init. -// - We don't want to depend on base::LazyInstance, which may be converted to -// static locals one day. -// -// Nevertheless, this provides essentially the same thing. -template <typename T, typename Constructor> -class LeakySingleton { - public: - constexpr LeakySingleton() = default; - - PA_ALWAYS_INLINE T* Get() { - auto* instance = instance_.load(std::memory_order_acquire); - if (PA_LIKELY(instance)) - return instance; - - return GetSlowPath(); - } - - // Replaces the instance pointer with a new one. - void Replace(T* new_instance) { - SimpleScopedSpinLocker scoped_lock{initialization_lock_}; - - // Modify under the lock to avoid race between |if (instance)| and - // |instance_.store()| in GetSlowPath(). - instance_.store(new_instance, std::memory_order_release); - } - - private: - T* GetSlowPath(); - - std::atomic<T*> instance_; - // Before C++20, having an initializer here causes a "variable does not have a - // constant initializer" error. In C++20, omitting it causes a similar error. - // Presumably this is due to the C++20 changes to make atomic initialization - // (of the other members of this class) sane, so guarding under that - // feature-test. -#if !defined(__cpp_lib_atomic_value_initialization) || \ - __cpp_lib_atomic_value_initialization < 201911L - alignas(T) uint8_t instance_buffer_[sizeof(T)]; -#else - alignas(T) uint8_t instance_buffer_[sizeof(T)] = {0}; -#endif - std::atomic<bool> initialization_lock_; -}; - -template <typename T, typename Constructor> -T* LeakySingleton<T, Constructor>::GetSlowPath() { - // The instance has not been set, the proper way to proceed (correct - // double-checked locking) is: - // - // auto* instance = instance_.load(std::memory_order_acquire); - // if (!instance) { - // ScopedLock initialization_lock; - // root = instance_.load(std::memory_order_relaxed); - // if (root) - // return root; - // instance = Create new root; - // instance_.store(instance, std::memory_order_release); - // return instance; - // } - // - // However, we don't want to use a base::Lock here, so instead we use - // compare-and-exchange on a lock variable, which provides the same - // guarantees. - SimpleScopedSpinLocker scoped_lock{initialization_lock_}; - - T* instance = instance_.load(std::memory_order_relaxed); - // Someone beat us. - if (instance) - return instance; - - instance = Constructor::New(reinterpret_cast<void*>(instance_buffer_)); - instance_.store(instance, std::memory_order_release); - - return instance; -} - -class MainPartitionConstructor { - public: - static partition_alloc::ThreadSafePartitionRoot* New(void* buffer) { - constexpr partition_alloc::PartitionOptions::ThreadCache thread_cache = -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - // Additional partitions may be created in ConfigurePartitions(). Since - // only one partition can have thread cache enabled, postpone the - // decision to turn the thread cache on until after that call. - // TODO(bartekn): Enable it here by default, once the "split-only" mode - // is no longer needed. - partition_alloc::PartitionOptions::ThreadCache::kDisabled; -#else // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - // Other tests, such as the ThreadCache tests create a thread cache, - // and only one is supported at a time. - partition_alloc::PartitionOptions::ThreadCache::kDisabled; -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - auto* new_root = new (buffer) partition_alloc::ThreadSafePartitionRoot({ - partition_alloc::PartitionOptions::AlignedAlloc::kAllowed, - thread_cache, - partition_alloc::PartitionOptions::Quarantine::kAllowed, - partition_alloc::PartitionOptions::Cookie::kAllowed, - partition_alloc::PartitionOptions::BackupRefPtr::kDisabled, - partition_alloc::PartitionOptions::BackupRefPtrZapping::kDisabled, - partition_alloc::PartitionOptions::UseConfigurablePool::kNo, - }); - - return new_root; - } -}; - -LeakySingleton<partition_alloc::ThreadSafePartitionRoot, - MainPartitionConstructor> - g_root PA_CONSTINIT = {}; -partition_alloc::ThreadSafePartitionRoot* Allocator() { - return g_root.Get(); -} - -// Original g_root_ if it was replaced by ConfigurePartitions(). -std::atomic<partition_alloc::ThreadSafePartitionRoot*> g_original_root(nullptr); - -class AlignedPartitionConstructor { - public: - static partition_alloc::ThreadSafePartitionRoot* New(void* buffer) { - return g_root.Get(); - } -}; - -LeakySingleton<partition_alloc::ThreadSafePartitionRoot, - AlignedPartitionConstructor> - g_aligned_root PA_CONSTINIT = {}; - -partition_alloc::ThreadSafePartitionRoot* OriginalAllocator() { - return g_original_root.load(std::memory_order_relaxed); -} - -partition_alloc::ThreadSafePartitionRoot* AlignedAllocator() { - return g_aligned_root.Get(); -} - -void* AllocateAlignedMemory(size_t alignment, size_t size) { - // Memory returned by the regular allocator *always* respects |kAlignment|, - // which is a power of two, and any valid alignment is also a power of two. So - // we can directly fulfill these requests with the main allocator. - // - // This has several advantages: - // - The thread cache is supported on the main partition - // - Reduced fragmentation - // - Better coverage for MiraclePtr variants requiring extras - // - // There are several call sites in Chromium where base::AlignedAlloc is called - // with a small alignment. Some may be due to overly-careful code, some are - // because the client code doesn't know the required alignment at compile - // time. - // - // Note that all "AlignedFree()" variants (_aligned_free() on Windows for - // instance) directly call PartitionFree(), so there is no risk of - // mismatch. (see below the default_dispatch definition). - if (alignment <= partition_alloc::internal::kAlignment) { - // This is mandated by |posix_memalign()| and friends, so should never fire. - PA_CHECK(partition_alloc::internal::base::bits::IsPowerOfTwo(alignment)); - // TODO(bartekn): See if the compiler optimizes branches down the stack on - // Mac, where PartitionPageSize() isn't constexpr. - return Allocator()->AllocWithFlagsNoHooks( - 0, size, partition_alloc::PartitionPageSize()); - } - - return AlignedAllocator()->AlignedAllocWithFlags( - partition_alloc::AllocFlags::kNoHooks, alignment, size); -} - -} // namespace - -namespace allocator_shim::internal { - -namespace { -#if BUILDFLAG(IS_APPLE) -unsigned int g_alloc_flags = 0; -#else -constexpr unsigned int g_alloc_flags = 0; -#endif -} // namespace - -void PartitionAllocSetCallNewHandlerOnMallocFailure(bool value) { -#if BUILDFLAG(IS_APPLE) - // We generally prefer to always crash rather than returning nullptr for - // OOM. However, on some macOS releases, we have to locally allow it due to - // weirdness in OS code. See https://crbug.com/654695 for details. - // - // Apple only since it's not needed elsewhere, and there is a performance - // penalty. - - if (value) - g_alloc_flags = 0; - else - g_alloc_flags = partition_alloc::AllocFlags::kReturnNull; -#endif -} - -void* PartitionMalloc(const AllocatorDispatch*, size_t size, void* context) { - partition_alloc::ScopedDisallowAllocations guard{}; - return Allocator()->AllocWithFlagsNoHooks( - g_alloc_flags, size, partition_alloc::PartitionPageSize()); -} - -void* PartitionMallocUnchecked(const AllocatorDispatch*, - size_t size, - void* context) { - partition_alloc::ScopedDisallowAllocations guard{}; - return Allocator()->AllocWithFlagsNoHooks( - partition_alloc::AllocFlags::kReturnNull | g_alloc_flags, size, - partition_alloc::PartitionPageSize()); -} - -void* PartitionCalloc(const AllocatorDispatch*, - size_t n, - size_t size, - void* context) { - partition_alloc::ScopedDisallowAllocations guard{}; - const size_t total = - partition_alloc::internal::base::CheckMul(n, size).ValueOrDie(); - return Allocator()->AllocWithFlagsNoHooks( - partition_alloc::AllocFlags::kZeroFill | g_alloc_flags, total, - partition_alloc::PartitionPageSize()); -} - -void* PartitionMemalign(const AllocatorDispatch*, - size_t alignment, - size_t size, - void* context) { - partition_alloc::ScopedDisallowAllocations guard{}; - return AllocateAlignedMemory(alignment, size); -} - -void* PartitionAlignedAlloc(const AllocatorDispatch* dispatch, - size_t size, - size_t alignment, - void* context) { - partition_alloc::ScopedDisallowAllocations guard{}; - return AllocateAlignedMemory(alignment, size); -} - -// aligned_realloc documentation is -// https://docs.microsoft.com/ja-jp/cpp/c-runtime-library/reference/aligned-realloc -// TODO(tasak): Expand the given memory block to the given size if possible. -// This realloc always free the original memory block and allocates a new memory -// block. -// TODO(tasak): Implement PartitionRoot<thread_safe>::AlignedReallocWithFlags -// and use it. -void* PartitionAlignedRealloc(const AllocatorDispatch* dispatch, - void* address, - size_t size, - size_t alignment, - void* context) { - partition_alloc::ScopedDisallowAllocations guard{}; - void* new_ptr = nullptr; - if (size > 0) { - new_ptr = AllocateAlignedMemory(alignment, size); - } else { - // size == 0 and address != null means just "free(address)". - if (address) - partition_alloc::ThreadSafePartitionRoot::FreeNoHooks(address); - } - // The original memory block (specified by address) is unchanged if ENOMEM. - if (!new_ptr) - return nullptr; - // TODO(tasak): Need to compare the new alignment with the address' alignment. - // If the two alignments are not the same, need to return nullptr with EINVAL. - if (address) { - size_t usage = - partition_alloc::ThreadSafePartitionRoot::GetUsableSize(address); - size_t copy_size = usage > size ? size : usage; - memcpy(new_ptr, address, copy_size); - - partition_alloc::ThreadSafePartitionRoot::FreeNoHooks(address); - } - return new_ptr; -} - -void* PartitionRealloc(const AllocatorDispatch*, - void* address, - size_t size, - void* context) { - partition_alloc::ScopedDisallowAllocations guard{}; -#if BUILDFLAG(IS_APPLE) - if (PA_UNLIKELY(!partition_alloc::IsManagedByPartitionAlloc( - reinterpret_cast<uintptr_t>(address)) && - address)) { - // A memory region allocated by the system allocator is passed in this - // function. Forward the request to `realloc` which supports zone- - // dispatching so that it appropriately selects the right zone. - return realloc(address, size); - } -#endif // BUILDFLAG(IS_APPLE) - - return Allocator()->ReallocWithFlags( - partition_alloc::AllocFlags::kNoHooks | g_alloc_flags, address, size, ""); -} - -#if BUILDFLAG(IS_CAST_ANDROID) -extern "C" { -void __real_free(void*); -} // extern "C" -#endif // BUILDFLAG(IS_CAST_ANDROID) - -void PartitionFree(const AllocatorDispatch*, void* object, void* context) { - partition_alloc::ScopedDisallowAllocations guard{}; -#if BUILDFLAG(IS_APPLE) - // TODO(bartekn): Add MTE unmasking here (and below). - if (PA_UNLIKELY(!partition_alloc::IsManagedByPartitionAlloc( - reinterpret_cast<uintptr_t>(object)) && - object)) { - // A memory region allocated by the system allocator is passed in this - // function. Forward the request to `free` which supports zone- - // dispatching so that it appropriately selects the right zone. - return free(object); - } -#endif // BUILDFLAG(IS_APPLE) - - // On Android Chromecast devices, there is at least one case where a system - // malloc() pointer can be passed to PartitionAlloc's free(). If we don't own - // the pointer, pass it along. This should not have a runtime cost vs regular - // Android, since on Android we have a PA_CHECK() rather than the branch here. -#if BUILDFLAG(IS_CAST_ANDROID) - if (PA_UNLIKELY(!partition_alloc::IsManagedByPartitionAlloc( - reinterpret_cast<uintptr_t>(object)) && - object)) { - // A memory region allocated by the system allocator is passed in this - // function. Forward the request to `free()`, which is `__real_free()` - // here. - return __real_free(object); - } -#endif // BUILDFLAG(IS_CAST_ANDROID) - - partition_alloc::ThreadSafePartitionRoot::FreeNoHooks(object); -} - -#if BUILDFLAG(IS_APPLE) -// Normal free() path on Apple OSes: -// 1. size = GetSizeEstimate(ptr); -// 2. if (size) FreeDefiniteSize(ptr, size) -// -// So we don't need to re-check that the pointer is owned in Free(), and we -// can use the size. -void PartitionFreeDefiniteSize(const AllocatorDispatch*, - void* address, - size_t size, - void* context) { - partition_alloc::ScopedDisallowAllocations guard{}; - // TODO(lizeb): Optimize PartitionAlloc to use the size information. This is - // still useful though, as we avoid double-checking that the address is owned. - partition_alloc::ThreadSafePartitionRoot::FreeNoHooks(address); -} -#endif // BUILDFLAG(IS_APPLE) - -size_t PartitionGetSizeEstimate(const AllocatorDispatch*, - void* address, - void* context) { - // This is used to implement malloc_usable_size(3). Per its man page, "if ptr - // is NULL, 0 is returned". - if (!address) - return 0; - -#if BUILDFLAG(IS_APPLE) - if (!partition_alloc::IsManagedByPartitionAlloc( - reinterpret_cast<uintptr_t>(address))) { - // The object pointed to by `address` is not allocated by the - // PartitionAlloc. The return value `0` means that the pointer does not - // belong to this malloc zone. - return 0; - } -#endif // BUILDFLAG(IS_APPLE) - - // TODO(lizeb): Returns incorrect values for aligned allocations. - const size_t size = partition_alloc::ThreadSafePartitionRoot:: - GetUsableSizeWithMac11MallocSizeHack(address); -#if BUILDFLAG(IS_APPLE) - // The object pointed to by `address` is allocated by the PartitionAlloc. - // So, this function must not return zero so that the malloc zone dispatcher - // finds the appropriate malloc zone. - PA_DCHECK(size); -#endif // BUILDFLAG(IS_APPLE) - return size; -} - -#if BUILDFLAG(IS_APPLE) -bool PartitionClaimedAddress(const AllocatorDispatch*, - void* address, - void* context) { - return partition_alloc::IsManagedByPartitionAlloc( - reinterpret_cast<uintptr_t>(address)); -} -#endif // BUILDFLAG(IS_APPLE) - -unsigned PartitionBatchMalloc(const AllocatorDispatch*, - size_t size, - void** results, - unsigned num_requested, - void* context) { - // No real batching: we could only acquire the lock once for instance, keep it - // simple for now. - for (unsigned i = 0; i < num_requested; i++) { - // No need to check the results, we crash if it fails. - results[i] = PartitionMalloc(nullptr, size, nullptr); - } - - // Either all succeeded, or we crashed. - return num_requested; -} - -void PartitionBatchFree(const AllocatorDispatch*, - void** to_be_freed, - unsigned num_to_be_freed, - void* context) { - // No real batching: we could only acquire the lock once for instance, keep it - // simple for now. - for (unsigned i = 0; i < num_to_be_freed; i++) { - PartitionFree(nullptr, to_be_freed[i], nullptr); - } -} - -#if BUILDFLAG(IS_APPLE) -void PartitionTryFreeDefault(const AllocatorDispatch*, - void* address, - void* context) { - partition_alloc::ScopedDisallowAllocations guard{}; - - if (UNLIKELY(!partition_alloc::IsManagedByPartitionAlloc( - reinterpret_cast<uintptr_t>(address)))) { - // The object pointed to by `address` is not allocated by the - // PartitionAlloc. Call find_zone_and_free. - return allocator_shim::TryFreeDefaultFallbackToFindZoneAndFree(address); - } - - partition_alloc::ThreadSafePartitionRoot::FreeNoHooks(address); -} -#endif // BUILDFLAG(IS_APPLE) - -// static -partition_alloc::ThreadSafePartitionRoot* PartitionAllocMalloc::Allocator() { - return ::Allocator(); -} - -// static -partition_alloc::ThreadSafePartitionRoot* -PartitionAllocMalloc::OriginalAllocator() { - return ::OriginalAllocator(); -} - -// static -partition_alloc::ThreadSafePartitionRoot* -PartitionAllocMalloc::AlignedAllocator() { - return ::AlignedAllocator(); -} - -} // namespace allocator_shim::internal - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - -namespace allocator_shim { - -void EnablePartitionAllocMemoryReclaimer() { - // Unlike other partitions, Allocator() and AlignedAllocator() do not register - // their PartitionRoots to the memory reclaimer, because doing so may allocate - // memory. Thus, the registration to the memory reclaimer has to be done - // some time later, when the main root is fully configured. - // TODO(bartekn): Aligned allocator can use the regular initialization path. - ::partition_alloc::MemoryReclaimer::Instance()->RegisterPartition( - Allocator()); - auto* original_root = OriginalAllocator(); - if (original_root) - ::partition_alloc::MemoryReclaimer::Instance()->RegisterPartition( - original_root); - if (AlignedAllocator() != Allocator()) { - ::partition_alloc::MemoryReclaimer::Instance()->RegisterPartition( - AlignedAllocator()); - } -} - -void ConfigurePartitions( - EnableBrp enable_brp, - EnableBrpZapping enable_brp_zapping, - EnableBrpPartitionMemoryReclaimer enable_brp_memory_reclaimer, - SplitMainPartition split_main_partition, - UseDedicatedAlignedPartition use_dedicated_aligned_partition, - AddDummyRefCount add_dummy_ref_count, - AlternateBucketDistribution use_alternate_bucket_distribution) { - // BRP cannot be enabled without splitting the main partition. Furthermore, in - // the "before allocation" mode, it can't be enabled without further splitting - // out the aligned partition. - PA_CHECK(!enable_brp || split_main_partition); -#if !BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) - PA_CHECK(!enable_brp || use_dedicated_aligned_partition); -#endif - // Can't split out the aligned partition, without splitting the main one. - PA_CHECK(!use_dedicated_aligned_partition || split_main_partition); - - static bool configured = false; - PA_CHECK(!configured); - configured = true; - - // Calling Get() is actually important, even if the return values weren't - // used, because it has a side effect of initializing the variables, if they - // weren't already. - auto* current_root = g_root.Get(); - auto* current_aligned_root = g_aligned_root.Get(); - - if (!split_main_partition) { - switch (use_alternate_bucket_distribution) { - case AlternateBucketDistribution::kDefault: - // We start in the 'default' case. - break; - case AlternateBucketDistribution::kDenser: - current_root->SwitchToDenserBucketDistribution(); - current_aligned_root->SwitchToDenserBucketDistribution(); - break; - } - PA_DCHECK(!enable_brp); - PA_DCHECK(!use_dedicated_aligned_partition); - PA_DCHECK(!current_root->flags.with_thread_cache); - return; - } - - // We've been bitten before by using a static local when initializing a - // partition. For synchronization, static local variables call into the - // runtime on Windows, which may not be ready to handle it, if the path is - // invoked on an allocation during the runtime initialization. - // ConfigurePartitions() is invoked explicitly from Chromium code, so this - // shouldn't bite us here. Mentioning just in case we move this code earlier. - static partition_alloc::internal::base::NoDestructor< - partition_alloc::ThreadSafePartitionRoot> - new_main_partition(partition_alloc::PartitionOptions( - !use_dedicated_aligned_partition - ? partition_alloc::PartitionOptions::AlignedAlloc::kAllowed - : partition_alloc::PartitionOptions::AlignedAlloc::kDisallowed, - partition_alloc::PartitionOptions::ThreadCache::kDisabled, - partition_alloc::PartitionOptions::Quarantine::kAllowed, - partition_alloc::PartitionOptions::Cookie::kAllowed, - enable_brp - ? partition_alloc::PartitionOptions::BackupRefPtr::kEnabled - : partition_alloc::PartitionOptions::BackupRefPtr::kDisabled, - enable_brp_zapping - ? partition_alloc::PartitionOptions::BackupRefPtrZapping::kEnabled - : partition_alloc::PartitionOptions::BackupRefPtrZapping:: - kDisabled, - partition_alloc::PartitionOptions::UseConfigurablePool::kNo, - add_dummy_ref_count - ? partition_alloc::PartitionOptions::AddDummyRefCount::kEnabled - : partition_alloc::PartitionOptions::AddDummyRefCount:: - kDisabled)); - partition_alloc::ThreadSafePartitionRoot* new_root = new_main_partition.get(); - - partition_alloc::ThreadSafePartitionRoot* new_aligned_root; - if (use_dedicated_aligned_partition) { - // TODO(bartekn): Use the original root instead of creating a new one. It'd - // result in one less partition, but come at a cost of commingling types. - static partition_alloc::internal::base::NoDestructor< - partition_alloc::ThreadSafePartitionRoot> - new_aligned_partition(partition_alloc::PartitionOptions{ - partition_alloc::PartitionOptions::AlignedAlloc::kAllowed, - partition_alloc::PartitionOptions::ThreadCache::kDisabled, - partition_alloc::PartitionOptions::Quarantine::kAllowed, - partition_alloc::PartitionOptions::Cookie::kAllowed, - partition_alloc::PartitionOptions::BackupRefPtr::kDisabled, - partition_alloc::PartitionOptions::BackupRefPtrZapping::kDisabled, - partition_alloc::PartitionOptions::UseConfigurablePool::kNo, - }); - new_aligned_root = new_aligned_partition.get(); - } else { - // The new main root can also support AlignedAlloc. - new_aligned_root = new_root; - } - - // Now switch traffic to the new partitions. - g_aligned_root.Replace(new_aligned_root); - g_root.Replace(new_root); - - // g_original_root has to be set after g_root, because other code doesn't - // handle well both pointing to the same root. - // TODO(bartekn): Reorder, once handled well. It isn't ideal for one - // partition to be invisible temporarily. - g_original_root = current_root; - - // No need for g_original_aligned_root, because in cases where g_aligned_root - // is replaced, it must've been g_original_root. - PA_CHECK(current_aligned_root == g_original_root); - - if (enable_brp_memory_reclaimer) { - partition_alloc::MemoryReclaimer::Instance()->RegisterPartition(new_root); - if (new_aligned_root != new_root) { - partition_alloc::MemoryReclaimer::Instance()->RegisterPartition( - new_aligned_root); - } - } - - // Purge memory, now that the traffic to the original partition is cut off. - current_root->PurgeMemory( - partition_alloc::PurgeFlags::kDecommitEmptySlotSpans | - partition_alloc::PurgeFlags::kDiscardUnusedSystemPages); - - switch (use_alternate_bucket_distribution) { - case AlternateBucketDistribution::kDefault: - // We start in the 'default' case. - break; - case AlternateBucketDistribution::kDenser: - g_root.Get()->SwitchToDenserBucketDistribution(); - g_aligned_root.Get()->SwitchToDenserBucketDistribution(); - break; - } -} - -#if BUILDFLAG(USE_STARSCAN) -void EnablePCScan(partition_alloc::internal::PCScan::InitConfig config) { - partition_alloc::internal::base::PlatformThread::SetThreadNameHook( - &::base::PlatformThread::SetName); - partition_alloc::internal::PCScan::Initialize(config); - - partition_alloc::internal::PCScan::RegisterScannableRoot(Allocator()); - if (OriginalAllocator() != nullptr) - partition_alloc::internal::PCScan::RegisterScannableRoot( - OriginalAllocator()); - if (Allocator() != AlignedAllocator()) - partition_alloc::internal::PCScan::RegisterScannableRoot( - AlignedAllocator()); - - base::internal::NonScannableAllocator::Instance().NotifyPCScanEnabled(); - base::internal::NonQuarantinableAllocator::Instance().NotifyPCScanEnabled(); -} -#endif // BUILDFLAG(USE_STARSCAN) -} // namespace allocator_shim - -const AllocatorDispatch AllocatorDispatch::default_dispatch = { - &allocator_shim::internal::PartitionMalloc, // alloc_function - &allocator_shim::internal:: - PartitionMallocUnchecked, // alloc_unchecked_function - &allocator_shim::internal:: - PartitionCalloc, // alloc_zero_initialized_function - &allocator_shim::internal::PartitionMemalign, // alloc_aligned_function - &allocator_shim::internal::PartitionRealloc, // realloc_function - &allocator_shim::internal::PartitionFree, // free_function - &allocator_shim::internal:: - PartitionGetSizeEstimate, // get_size_estimate_function -#if BUILDFLAG(IS_APPLE) - &allocator_shim::internal::PartitionClaimedAddress, // claimed_address -#else - nullptr, // claimed_address -#endif - &allocator_shim::internal::PartitionBatchMalloc, // batch_malloc_function - &allocator_shim::internal::PartitionBatchFree, // batch_free_function -#if BUILDFLAG(IS_APPLE) - // On Apple OSes, free_definite_size() is always called from free(), since - // get_size_estimate() is used to determine whether an allocation belongs to - // the current zone. It makes sense to optimize for it. - &allocator_shim::internal::PartitionFreeDefiniteSize, - // On Apple OSes, try_free_default() is sometimes called as an optimization - // of free(). - &allocator_shim::internal::PartitionTryFreeDefault, -#else - nullptr, // free_definite_size_function - nullptr, // try_free_default_function -#endif - &allocator_shim::internal:: - PartitionAlignedAlloc, // aligned_malloc_function - &allocator_shim::internal:: - PartitionAlignedRealloc, // aligned_realloc_function - &allocator_shim::internal::PartitionFree, // aligned_free_function - nullptr, // next -}; - -// Intercept diagnostics symbols as well, even though they are not part of the -// unified shim layer. -// -// TODO(lizeb): Implement the ones that doable. - -extern "C" { - -#if !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_ANDROID) - -SHIM_ALWAYS_EXPORT void malloc_stats(void) __THROW {} - -SHIM_ALWAYS_EXPORT int mallopt(int cmd, int value) __THROW { - return 0; -} - -#endif // !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_ANDROID) - -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) -SHIM_ALWAYS_EXPORT struct mallinfo mallinfo(void) __THROW { - partition_alloc::SimplePartitionStatsDumper allocator_dumper; - Allocator()->DumpStats("malloc", true, &allocator_dumper); - // TODO(bartekn): Dump OriginalAllocator() into "malloc" as well. - - partition_alloc::SimplePartitionStatsDumper aligned_allocator_dumper; - if (AlignedAllocator() != Allocator()) { - AlignedAllocator()->DumpStats("posix_memalign", true, - &aligned_allocator_dumper); - } - - // Dump stats for nonscannable and nonquarantinable allocators. - auto& nonscannable_allocator = - base::internal::NonScannableAllocator::Instance(); - partition_alloc::SimplePartitionStatsDumper nonscannable_allocator_dumper; - if (auto* nonscannable_root = nonscannable_allocator.root()) - nonscannable_root->DumpStats("malloc", true, - &nonscannable_allocator_dumper); - auto& nonquarantinable_allocator = - base::internal::NonQuarantinableAllocator::Instance(); - partition_alloc::SimplePartitionStatsDumper nonquarantinable_allocator_dumper; - if (auto* nonquarantinable_root = nonquarantinable_allocator.root()) - nonquarantinable_root->DumpStats("malloc", true, - &nonquarantinable_allocator_dumper); - - struct mallinfo info = {0}; - info.arena = 0; // Memory *not* allocated with mmap(). - - // Memory allocated with mmap(), aka virtual size. - info.hblks = - partition_alloc::internal::base::checked_cast<decltype(info.hblks)>( - allocator_dumper.stats().total_mmapped_bytes + - aligned_allocator_dumper.stats().total_mmapped_bytes + - nonscannable_allocator_dumper.stats().total_mmapped_bytes + - nonquarantinable_allocator_dumper.stats().total_mmapped_bytes); - // Resident bytes. - info.hblkhd = - partition_alloc::internal::base::checked_cast<decltype(info.hblkhd)>( - allocator_dumper.stats().total_resident_bytes + - aligned_allocator_dumper.stats().total_resident_bytes + - nonscannable_allocator_dumper.stats().total_resident_bytes + - nonquarantinable_allocator_dumper.stats().total_resident_bytes); - // Allocated bytes. - info.uordblks = - partition_alloc::internal::base::checked_cast<decltype(info.uordblks)>( - allocator_dumper.stats().total_active_bytes + - aligned_allocator_dumper.stats().total_active_bytes + - nonscannable_allocator_dumper.stats().total_active_bytes + - nonquarantinable_allocator_dumper.stats().total_active_bytes); - - return info; -} -#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) - -} // extern "C" - -#if BUILDFLAG(IS_APPLE) - -namespace allocator_shim { - -void InitializeDefaultAllocatorPartitionRoot() { - // On OS_APPLE, the initialization of PartitionRoot uses memory allocations - // internally, e.g. __builtin_available, and it's not easy to avoid it. - // Thus, we initialize the PartitionRoot with using the system default - // allocator before we intercept the system default allocator. - std::ignore = Allocator(); -} - -} // namespace allocator_shim - -#endif // BUILDFLAG(IS_APPLE) - -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
diff --git a/base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_partition_alloc.h b/base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_partition_alloc.h deleted file mode 100644 index f643e0a..0000000 --- a/base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_partition_alloc.h +++ /dev/null
@@ -1,69 +0,0 @@ -// 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 BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_DEFAULT_DISPATCH_TO_PARTITION_ALLOC_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_DEFAULT_DISPATCH_TO_PARTITION_ALLOC_H_ - -#include "base/allocator/partition_allocator/partition_alloc.h" -#include "base/allocator/partition_allocator/shim/allocator_shim.h" -#include "base/base_export.h" - -namespace allocator_shim::internal { - -void PartitionAllocSetCallNewHandlerOnMallocFailure(bool value); - -class BASE_EXPORT PartitionAllocMalloc { - public: - static partition_alloc::ThreadSafePartitionRoot* Allocator(); - // May return |nullptr|, will never return the same pointer as |Allocator()|. - static partition_alloc::ThreadSafePartitionRoot* OriginalAllocator(); - // May return the same pointer as |Allocator()|. - static partition_alloc::ThreadSafePartitionRoot* AlignedAllocator(); -}; - -BASE_EXPORT void* PartitionMalloc(const AllocatorDispatch*, - size_t size, - void* context); - -BASE_EXPORT void* PartitionMallocUnchecked(const AllocatorDispatch*, - size_t size, - void* context); - -BASE_EXPORT void* PartitionCalloc(const AllocatorDispatch*, - size_t n, - size_t size, - void* context); - -BASE_EXPORT void* PartitionMemalign(const AllocatorDispatch*, - size_t alignment, - size_t size, - void* context); - -BASE_EXPORT void* PartitionAlignedAlloc(const AllocatorDispatch* dispatch, - size_t size, - size_t alignment, - void* context); - -BASE_EXPORT void* PartitionAlignedRealloc(const AllocatorDispatch* dispatch, - void* address, - size_t size, - size_t alignment, - void* context); - -BASE_EXPORT void* PartitionRealloc(const AllocatorDispatch*, - void* address, - size_t size, - void* context); - -BASE_EXPORT void PartitionFree(const AllocatorDispatch*, - void* object, - void* context); - -BASE_EXPORT size_t PartitionGetSizeEstimate(const AllocatorDispatch*, - void* address, - void* context); - -} // namespace allocator_shim::internal - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_DEFAULT_DISPATCH_TO_PARTITION_ALLOC_H_
diff --git a/base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_partition_alloc_unittest.cc b/base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_partition_alloc_unittest.cc deleted file mode 100644 index 0dcc19c..0000000 --- a/base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_partition_alloc_unittest.cc +++ /dev/null
@@ -1,205 +0,0 @@ -// 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. - -#include "base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_partition_alloc.h" - -#include <cstdlib> -#include <cstring> - -#include "base/allocator/buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "base/memory/page_size.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) -#include <malloc.h> -#endif - -#if !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) && BUILDFLAG(USE_PARTITION_ALLOC) -namespace allocator_shim::internal { - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - -// Platforms on which we override weak libc symbols. -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) - -PA_NOINLINE void FreeForTest(void* data) { - free(data); -} - -TEST(PartitionAllocAsMalloc, Mallinfo) { - // mallinfo was deprecated in glibc 2.33. The Chrome OS device sysroot has - // a new-enough glibc, but the Linux one doesn't yet, so we can't switch to - // the replacement mallinfo2 yet. - // Once we update the Linux sysroot to be new enough, this warning will - // start firing on Linux too. At that point, s/mallinfo/mallinfo2/ in this - // file and remove the pragma here and and the end of this function. -#if BUILDFLAG(IS_CHROMEOS) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -#endif - constexpr int kLargeAllocSize = 10 * 1024 * 1024; - struct mallinfo before = mallinfo(); - void* data = malloc(1000); - ASSERT_TRUE(data); - void* aligned_data; - ASSERT_EQ(0, posix_memalign(&aligned_data, 1024, 1000)); - ASSERT_TRUE(aligned_data); - void* direct_mapped_data = malloc(kLargeAllocSize); - ASSERT_TRUE(direct_mapped_data); - struct mallinfo after_alloc = mallinfo(); - - // Something is reported. - EXPECT_GT(after_alloc.hblks, 0); - EXPECT_GT(after_alloc.hblkhd, 0); - EXPECT_GT(after_alloc.uordblks, 0); - - EXPECT_GT(after_alloc.hblks, kLargeAllocSize); - - // malloc() can reuse memory, so sizes are not necessarily changing, which - // would mean that we need EXPECT_G*E*() rather than EXPECT_GT(). - // - // However since we allocate direct-mapped memory, this increases the total. - EXPECT_GT(after_alloc.hblks, before.hblks); - EXPECT_GT(after_alloc.hblkhd, before.hblkhd); - EXPECT_GT(after_alloc.uordblks, before.uordblks); - - // a simple malloc() / free() pair can be discarded by the compiler (and is), - // making the test fail. It is sufficient to make |FreeForTest()| a - // PA_NOINLINE function for the call to not be eliminated, but this is - // required. - FreeForTest(data); - FreeForTest(aligned_data); - FreeForTest(direct_mapped_data); - struct mallinfo after_free = mallinfo(); - - EXPECT_LT(after_free.hblks, after_alloc.hblks); - EXPECT_LT(after_free.hblkhd, after_alloc.hblkhd); - EXPECT_LT(after_free.uordblks, after_alloc.uordblks); -#if BUILDFLAG(IS_CHROMEOS) -#pragma clang diagnostic pop -#endif -} - -#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) - -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - -// Note: the tests below are quite simple, they are used as simple smoke tests -// for PartitionAlloc-Everywhere. Most of these directly dispatch to -// PartitionAlloc, which has much more extensive tests. -TEST(PartitionAllocAsMalloc, Simple) { - void* data = PartitionMalloc(nullptr, 10, nullptr); - EXPECT_TRUE(data); - PartitionFree(nullptr, data, nullptr); -} - -TEST(PartitionAllocAsMalloc, MallocUnchecked) { - void* data = PartitionMallocUnchecked(nullptr, 10, nullptr); - EXPECT_TRUE(data); - PartitionFree(nullptr, data, nullptr); - - void* too_large = PartitionMallocUnchecked(nullptr, 4e9, nullptr); - EXPECT_FALSE(too_large); // No crash. -} - -TEST(PartitionAllocAsMalloc, Calloc) { - constexpr size_t alloc_size = 100; - void* data = PartitionCalloc(nullptr, 1, alloc_size, nullptr); - EXPECT_TRUE(data); - - char* zeroes[alloc_size]; - memset(zeroes, 0, alloc_size); - - EXPECT_EQ(0, memcmp(zeroes, data, alloc_size)); - PartitionFree(nullptr, data, nullptr); -} - -TEST(PartitionAllocAsMalloc, Memalign) { - constexpr size_t alloc_size = 100; - constexpr size_t alignment = 1024; - void* data = PartitionMemalign(nullptr, alignment, alloc_size, nullptr); - EXPECT_TRUE(data); - EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(data) % alignment); - PartitionFree(nullptr, data, nullptr); -} - -TEST(PartitionAllocAsMalloc, AlignedAlloc) { - for (size_t alloc_size : {100, 100000, 10000000}) { - for (size_t alignment = 1; - alignment <= partition_alloc::kMaxSupportedAlignment; - alignment <<= 1) { - void* data = - PartitionAlignedAlloc(nullptr, alloc_size, alignment, nullptr); - EXPECT_TRUE(data); - EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(data) % alignment); - PartitionFree(nullptr, data, nullptr); - } - } -} - -TEST(PartitionAllocAsMalloc, AlignedRealloc) { - for (size_t alloc_size : {100, 100000, 10000000}) { - for (size_t alignment = 1; - alignment <= partition_alloc::kMaxSupportedAlignment; - alignment <<= 1) { - void* data = - PartitionAlignedAlloc(nullptr, alloc_size, alignment, nullptr); - EXPECT_TRUE(data); - - void* data2 = PartitionAlignedRealloc(nullptr, data, alloc_size, - alignment, nullptr); - EXPECT_TRUE(data2); - - // Aligned realloc always relocates. - EXPECT_NE(reinterpret_cast<uintptr_t>(data), - reinterpret_cast<uintptr_t>(data2)); - PartitionFree(nullptr, data2, nullptr); - } - } -} - -TEST(PartitionAllocAsMalloc, Realloc) { - constexpr size_t alloc_size = 100; - void* data = PartitionMalloc(nullptr, alloc_size, nullptr); - EXPECT_TRUE(data); - void* data2 = PartitionMalloc(nullptr, 2 * alloc_size, nullptr); - EXPECT_TRUE(data2); - EXPECT_NE(data2, data); - PartitionFree(nullptr, data2, nullptr); -} - -// crbug.com/1141752 -TEST(PartitionAllocAsMalloc, Alignment) { - EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(PartitionAllocMalloc::Allocator()) % - alignof(partition_alloc::ThreadSafePartitionRoot)); - // This works fine even if nullptr is returned. - EXPECT_EQ(0u, reinterpret_cast<uintptr_t>( - PartitionAllocMalloc::OriginalAllocator()) % - alignof(partition_alloc::ThreadSafePartitionRoot)); - EXPECT_EQ(0u, reinterpret_cast<uintptr_t>( - PartitionAllocMalloc::AlignedAllocator()) % - alignof(partition_alloc::ThreadSafePartitionRoot)); -} - -// crbug.com/1297945 -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && BUILDFLAG(IS_APPLE) -TEST(PartitionAllocAsMalloc, DisableCrashOnOom) { - PartitionAllocSetCallNewHandlerOnMallocFailure(false); - // Smaller than the max size to avoid overflow checks with padding. - void* ptr = PartitionMalloc( - nullptr, std::numeric_limits<size_t>::max() - 10 * base::GetPageSize(), - nullptr); - // Should not crash. - EXPECT_FALSE(ptr); - PartitionAllocSetCallNewHandlerOnMallocFailure(true); -} -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && BUILDFLAG(IS_APPLE) - -} // namespace allocator_shim::internal -#endif // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) && - // BUILDFLAG(USE_PARTITION_ALLOC)
diff --git a/base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_winheap.cc b/base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_winheap.cc deleted file mode 100644 index adc15be..0000000 --- a/base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_winheap.cc +++ /dev/null
@@ -1,108 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/shim/allocator_shim.h" - -#include <ostream> - -#include "base/allocator/partition_allocator/partition_alloc_check.h" -#include "base/allocator/partition_allocator/shim/winheap_stubs_win.h" - -namespace { - -using allocator_shim::AllocatorDispatch; - -void* DefaultWinHeapMallocImpl(const AllocatorDispatch*, - size_t size, - void* context) { - return allocator_shim::WinHeapMalloc(size); -} - -void* DefaultWinHeapCallocImpl(const AllocatorDispatch* self, - size_t n, - size_t elem_size, - void* context) { - // Overflow check. - const size_t size = n * elem_size; - if (elem_size != 0 && size / elem_size != n) - return nullptr; - - void* result = DefaultWinHeapMallocImpl(self, size, context); - if (result) { - memset(result, 0, size); - } - return result; -} - -void* DefaultWinHeapMemalignImpl(const AllocatorDispatch* self, - size_t alignment, - size_t size, - void* context) { - PA_CHECK(false) << "The windows heap does not support memalign."; - return nullptr; -} - -void* DefaultWinHeapReallocImpl(const AllocatorDispatch* self, - void* address, - size_t size, - void* context) { - return allocator_shim::WinHeapRealloc(address, size); -} - -void DefaultWinHeapFreeImpl(const AllocatorDispatch*, - void* address, - void* context) { - allocator_shim::WinHeapFree(address); -} - -size_t DefaultWinHeapGetSizeEstimateImpl(const AllocatorDispatch*, - void* address, - void* context) { - return allocator_shim::WinHeapGetSizeEstimate(address); -} - -void* DefaultWinHeapAlignedMallocImpl(const AllocatorDispatch*, - size_t size, - size_t alignment, - void* context) { - return allocator_shim::WinHeapAlignedMalloc(size, alignment); -} - -void* DefaultWinHeapAlignedReallocImpl(const AllocatorDispatch*, - void* ptr, - size_t size, - size_t alignment, - void* context) { - return allocator_shim::WinHeapAlignedRealloc(ptr, size, alignment); -} - -void DefaultWinHeapAlignedFreeImpl(const AllocatorDispatch*, - void* ptr, - void* context) { - allocator_shim::WinHeapAlignedFree(ptr); -} - -} // namespace - -// Guarantee that default_dispatch is compile-time initialized to avoid using -// it before initialization (allocations before main in release builds with -// optimizations disabled). -constexpr AllocatorDispatch AllocatorDispatch::default_dispatch = { - &DefaultWinHeapMallocImpl, - &DefaultWinHeapMallocImpl, /* alloc_unchecked_function */ - &DefaultWinHeapCallocImpl, - &DefaultWinHeapMemalignImpl, - &DefaultWinHeapReallocImpl, - &DefaultWinHeapFreeImpl, - &DefaultWinHeapGetSizeEstimateImpl, - nullptr, /* claimed_address */ - nullptr, /* batch_malloc_function */ - nullptr, /* batch_free_function */ - nullptr, /* free_definite_size_function */ - nullptr, /* try_free_default_function */ - &DefaultWinHeapAlignedMallocImpl, - &DefaultWinHeapAlignedReallocImpl, - &DefaultWinHeapAlignedFreeImpl, - nullptr, /* next */ -};
diff --git a/base/allocator/partition_allocator/shim/allocator_shim_internals.h b/base/allocator/partition_allocator/shim/allocator_shim_internals.h deleted file mode 100644 index 8bddea7..0000000 --- a/base/allocator/partition_allocator/shim/allocator_shim_internals.h +++ /dev/null
@@ -1,53 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_INTERNALS_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_INTERNALS_H_ - -#include "build/build_config.h" - -#if defined(__GNUC__) - -#if BUILDFLAG(IS_POSIX) -#include <sys/cdefs.h> // for __THROW -#endif - -#ifndef __THROW // Not a glibc system -#ifdef _NOEXCEPT // LLVM libc++ uses noexcept instead -#define __THROW _NOEXCEPT -#else -#define __THROW -#endif // !_NOEXCEPT -#endif - -// Shim layer symbols need to be ALWAYS exported, regardless of component build. -// -// If an exported symbol is linked into a DSO, it may be preempted by a -// definition in the main executable. If this happens to an allocator symbol, it -// will mean that the DSO will use the main executable's allocator. This is -// normally relatively harmless -- regular allocations should all use the same -// allocator, but if the DSO tries to hook the allocator it will not see any -// allocations. -// -// However, if LLVM LTO is enabled, the compiler may inline the shim layer -// symbols into callers. The end result is that allocator calls in DSOs may use -// either the main executable's allocator or the DSO's allocator, depending on -// whether the call was inlined. This is arguably a bug in LLVM caused by its -// somewhat irregular handling of symbol interposition (see llvm.org/PR23501). -// To work around the bug we use noinline to prevent the symbols from being -// inlined. -// -// In the long run we probably want to avoid linking the allocator bits into -// DSOs altogether. This will save a little space and stop giving DSOs the false -// impression that they can hook the allocator. -#define SHIM_ALWAYS_EXPORT __attribute__((visibility("default"), noinline)) - -#elif BUILDFLAG(IS_WIN) // __GNUC__ - -#define __THROW -#define SHIM_ALWAYS_EXPORT __declspec(noinline) - -#endif // __GNUC__ - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_INTERNALS_H_
diff --git a/base/allocator/partition_allocator/shim/allocator_shim_override_cpp_symbols.h b/base/allocator/partition_allocator/shim/allocator_shim_override_cpp_symbols.h deleted file mode 100644 index 33bfeda..0000000 --- a/base/allocator/partition_allocator/shim/allocator_shim_override_cpp_symbols.h +++ /dev/null
@@ -1,134 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifdef BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_OVERRIDE_CPP_SYMBOLS_H_ -#error This header is meant to be included only once by allocator_shim.cc -#endif - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_OVERRIDE_CPP_SYMBOLS_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_OVERRIDE_CPP_SYMBOLS_H_ - -// Preempt the default new/delete C++ symbols so they call the shim entry -// points. This file is strongly inspired by tcmalloc's -// libc_override_redefine.h. - -#include <new> - -#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h" -#include "base/allocator/partition_allocator/shim/allocator_shim_internals.h" -#include "build/build_config.h" - -#if !BUILDFLAG(IS_APPLE) -#define SHIM_CPP_SYMBOLS_EXPORT SHIM_ALWAYS_EXPORT -#else -// On Apple OSes, prefer not exporting these symbols (as this reverts to the -// default behavior, they are still exported in e.g. component builds). This is -// partly due to intentional limits on exported symbols in the main library, but -// it is also needless, since no library used on macOS imports these. -// -// TODO(lizeb): It may not be necessary anywhere to export these. -#define SHIM_CPP_SYMBOLS_EXPORT PA_NOINLINE -#endif - -SHIM_CPP_SYMBOLS_EXPORT void* operator new(size_t size) { - return ShimCppNew(size); -} - -SHIM_CPP_SYMBOLS_EXPORT void operator delete(void* p) __THROW { - ShimCppDelete(p); -} - -SHIM_CPP_SYMBOLS_EXPORT void* operator new[](size_t size) { - return ShimCppNew(size); -} - -SHIM_CPP_SYMBOLS_EXPORT void operator delete[](void* p) __THROW { - ShimCppDelete(p); -} - -SHIM_CPP_SYMBOLS_EXPORT void* operator new(size_t size, - const std::nothrow_t&) __THROW { - return ShimCppNewNoThrow(size); -} - -SHIM_CPP_SYMBOLS_EXPORT void* operator new[](size_t size, - const std::nothrow_t&) __THROW { - return ShimCppNewNoThrow(size); -} - -SHIM_CPP_SYMBOLS_EXPORT void operator delete(void* p, - const std::nothrow_t&) __THROW { - ShimCppDelete(p); -} - -SHIM_CPP_SYMBOLS_EXPORT void operator delete[](void* p, - const std::nothrow_t&) __THROW { - ShimCppDelete(p); -} - -SHIM_CPP_SYMBOLS_EXPORT void operator delete(void* p, size_t) __THROW { - ShimCppDelete(p); -} - -SHIM_CPP_SYMBOLS_EXPORT void operator delete[](void* p, size_t) __THROW { - ShimCppDelete(p); -} - -SHIM_CPP_SYMBOLS_EXPORT void* operator new(std::size_t size, - std::align_val_t alignment) { - return ShimCppAlignedNew(size, static_cast<size_t>(alignment)); -} - -SHIM_CPP_SYMBOLS_EXPORT void* operator new(std::size_t size, - std::align_val_t alignment, - const std::nothrow_t&) __THROW { - return ShimCppAlignedNew(size, static_cast<size_t>(alignment)); -} - -SHIM_CPP_SYMBOLS_EXPORT void operator delete(void* p, - std::align_val_t) __THROW { - ShimCppDelete(p); -} - -SHIM_CPP_SYMBOLS_EXPORT void operator delete(void* p, - std::size_t size, - std::align_val_t) __THROW { - ShimCppDelete(p); -} - -SHIM_CPP_SYMBOLS_EXPORT void operator delete(void* p, - std::align_val_t, - const std::nothrow_t&) __THROW { - ShimCppDelete(p); -} - -SHIM_CPP_SYMBOLS_EXPORT void* operator new[](std::size_t size, - std::align_val_t alignment) { - return ShimCppAlignedNew(size, static_cast<size_t>(alignment)); -} - -SHIM_CPP_SYMBOLS_EXPORT void* operator new[](std::size_t size, - std::align_val_t alignment, - const std::nothrow_t&) __THROW { - return ShimCppAlignedNew(size, static_cast<size_t>(alignment)); -} - -SHIM_CPP_SYMBOLS_EXPORT void operator delete[](void* p, - std::align_val_t) __THROW { - ShimCppDelete(p); -} - -SHIM_CPP_SYMBOLS_EXPORT void operator delete[](void* p, - std::size_t size, - std::align_val_t) __THROW { - ShimCppDelete(p); -} - -SHIM_CPP_SYMBOLS_EXPORT void operator delete[](void* p, - std::align_val_t, - const std::nothrow_t&) __THROW { - ShimCppDelete(p); -} - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_OVERRIDE_CPP_SYMBOLS_H_
diff --git a/base/allocator/partition_allocator/shim/allocator_shim_override_glibc_weak_symbols.h b/base/allocator/partition_allocator/shim/allocator_shim_override_glibc_weak_symbols.h deleted file mode 100644 index 257a911..0000000 --- a/base/allocator/partition_allocator/shim/allocator_shim_override_glibc_weak_symbols.h +++ /dev/null
@@ -1,123 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifdef BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_OVERRIDE_GLIBC_WEAK_SYMBOLS_H_ -#error This header is meant to be included only once by allocator_shim.cc -#endif - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_OVERRIDE_GLIBC_WEAK_SYMBOLS_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_OVERRIDE_GLIBC_WEAK_SYMBOLS_H_ - -// Alias the internal Glibc symbols to the shim entry points. -// This file is strongly inspired by tcmalloc's libc_override_glibc.h. -// Effectively this file does two things: -// 1) Re-define the __malloc_hook & co symbols. Those symbols are defined as -// weak in glibc and are meant to be defined strongly by client processes -// to hook calls initiated from within glibc. -// 2) Re-define Glibc-specific symbols (__libc_malloc). The historical reason -// is that in the past (in RedHat 9) we had instances of libraries that were -// allocating via malloc() and freeing using __libc_free(). -// See tcmalloc's libc_override_glibc.h for more context. - -#include <features.h> // for __GLIBC__ -#include <malloc.h> -#include <unistd.h> - -#include <new> - -#include "base/allocator/partition_allocator/shim/allocator_shim_internals.h" - -// __MALLOC_HOOK_VOLATILE not defined in all Glibc headers. -#if !defined(__MALLOC_HOOK_VOLATILE) -#define MALLOC_HOOK_MAYBE_VOLATILE /**/ -#else -#define MALLOC_HOOK_MAYBE_VOLATILE __MALLOC_HOOK_VOLATILE -#endif - -extern "C" { - -// 1) Re-define malloc_hook weak symbols. -namespace { - -void* GlibcMallocHook(size_t size, const void* caller) { - return ShimMalloc(size, nullptr); -} - -void* GlibcReallocHook(void* ptr, size_t size, const void* caller) { - return ShimRealloc(ptr, size, nullptr); -} - -void GlibcFreeHook(void* ptr, const void* caller) { - return ShimFree(ptr, nullptr); -} - -void* GlibcMemalignHook(size_t align, size_t size, const void* caller) { - return ShimMemalign(align, size, nullptr); -} - -} // namespace - -__attribute__((visibility("default"))) void* ( - *MALLOC_HOOK_MAYBE_VOLATILE __malloc_hook)(size_t, - const void*) = &GlibcMallocHook; - -__attribute__((visibility("default"))) void* ( - *MALLOC_HOOK_MAYBE_VOLATILE __realloc_hook)(void*, size_t, const void*) = - &GlibcReallocHook; - -__attribute__((visibility("default"))) void ( - *MALLOC_HOOK_MAYBE_VOLATILE __free_hook)(void*, - const void*) = &GlibcFreeHook; - -__attribute__((visibility("default"))) void* ( - *MALLOC_HOOK_MAYBE_VOLATILE __memalign_hook)(size_t, size_t, const void*) = - &GlibcMemalignHook; - -// 2) Redefine libc symbols themselves. - -SHIM_ALWAYS_EXPORT void* __libc_malloc(size_t size) { - return ShimMalloc(size, nullptr); -} - -SHIM_ALWAYS_EXPORT void __libc_free(void* ptr) { - ShimFree(ptr, nullptr); -} - -SHIM_ALWAYS_EXPORT void* __libc_realloc(void* ptr, size_t size) { - return ShimRealloc(ptr, size, nullptr); -} - -SHIM_ALWAYS_EXPORT void* __libc_calloc(size_t n, size_t size) { - return ShimCalloc(n, size, nullptr); -} - -SHIM_ALWAYS_EXPORT void __libc_cfree(void* ptr) { - return ShimFree(ptr, nullptr); -} - -SHIM_ALWAYS_EXPORT void* __libc_memalign(size_t align, size_t s) { - return ShimMemalign(align, s, nullptr); -} - -SHIM_ALWAYS_EXPORT void* __libc_valloc(size_t size) { - return ShimValloc(size, nullptr); -} - -SHIM_ALWAYS_EXPORT void* __libc_pvalloc(size_t size) { - return ShimPvalloc(size); -} - -SHIM_ALWAYS_EXPORT int __posix_memalign(void** r, size_t a, size_t s) { - return ShimPosixMemalign(r, a, s); -} - -} // extern "C" - -// Safety check. -#if !defined(__GLIBC__) -#error The target platform does not seem to use Glibc. Disable the allocator \ -shim by setting use_allocator_shim=false in GN args. -#endif - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_OVERRIDE_GLIBC_WEAK_SYMBOLS_H_
diff --git a/base/allocator/partition_allocator/shim/allocator_shim_override_libc_symbols.h b/base/allocator/partition_allocator/shim/allocator_shim_override_libc_symbols.h deleted file mode 100644 index bb07170..0000000 --- a/base/allocator/partition_allocator/shim/allocator_shim_override_libc_symbols.h +++ /dev/null
@@ -1,92 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Its purpose is to preempt the Libc symbols for malloc/new so they call the -// shim layer entry points. - -#ifdef BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_OVERRIDE_LIBC_SYMBOLS_H_ -#error This header is meant to be included only once by allocator_shim.cc -#endif - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_OVERRIDE_LIBC_SYMBOLS_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_OVERRIDE_LIBC_SYMBOLS_H_ - -#include "build/build_config.h" - -#if BUILDFLAG(IS_APPLE) -#include <malloc/malloc.h> -#else -#include <malloc.h> -#endif - -#include "base/allocator/partition_allocator/shim/allocator_shim_internals.h" - -extern "C" { - -// WARNING: Whenever a new function is added there (which, surprisingly enough, -// happens. For instance glibc 2.33 introduced mallinfo2(), which we don't -// support... yet?), it MUST be added to build/linux/chrome.map. -// -// Otherwise the new symbol is not exported from Chromium's main binary, which -// is necessary to override libc's weak symbol, which in turn is necessary to -// intercept calls made by dynamic libraries. See crbug.com/1292206 for such -// an example. - -SHIM_ALWAYS_EXPORT void* malloc(size_t size) __THROW { - return ShimMalloc(size, nullptr); -} - -SHIM_ALWAYS_EXPORT void free(void* ptr) __THROW { - ShimFree(ptr, nullptr); -} - -SHIM_ALWAYS_EXPORT void* realloc(void* ptr, size_t size) __THROW { - return ShimRealloc(ptr, size, nullptr); -} - -SHIM_ALWAYS_EXPORT void* calloc(size_t n, size_t size) __THROW { - return ShimCalloc(n, size, nullptr); -} - -SHIM_ALWAYS_EXPORT void cfree(void* ptr) __THROW { - ShimFree(ptr, nullptr); -} - -SHIM_ALWAYS_EXPORT void* memalign(size_t align, size_t s) __THROW { - return ShimMemalign(align, s, nullptr); -} - -SHIM_ALWAYS_EXPORT void* aligned_alloc(size_t align, size_t s) __THROW { - return ShimMemalign(align, s, nullptr); -} - -SHIM_ALWAYS_EXPORT void* valloc(size_t size) __THROW { - return ShimValloc(size, nullptr); -} - -SHIM_ALWAYS_EXPORT void* pvalloc(size_t size) __THROW { - return ShimPvalloc(size); -} - -SHIM_ALWAYS_EXPORT int posix_memalign(void** r, size_t a, size_t s) __THROW { - return ShimPosixMemalign(r, a, s); -} - -SHIM_ALWAYS_EXPORT size_t malloc_size(const void* address) __THROW { - return ShimGetSizeEstimate(address, nullptr); -} - -SHIM_ALWAYS_EXPORT size_t malloc_usable_size(void* address) __THROW { - return ShimGetSizeEstimate(address, nullptr); -} - -// The default dispatch translation unit has to define also the following -// symbols (unless they are ultimately routed to the system symbols): -// void malloc_stats(void); -// int mallopt(int, int); -// struct mallinfo mallinfo(void); - -} // extern "C" - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_OVERRIDE_LIBC_SYMBOLS_H_
diff --git a/base/allocator/partition_allocator/shim/allocator_shim_override_linker_wrapped_symbols.h b/base/allocator/partition_allocator/shim/allocator_shim_override_linker_wrapped_symbols.h deleted file mode 100644 index de2af6d..0000000 --- a/base/allocator/partition_allocator/shim/allocator_shim_override_linker_wrapped_symbols.h +++ /dev/null
@@ -1,171 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifdef BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_OVERRIDE_LINKER_WRAPPED_SYMBOLS_H_ -#error This header is meant to be included only once by allocator_shim.cc -#endif - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_OVERRIDE_LINKER_WRAPPED_SYMBOLS_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_OVERRIDE_LINKER_WRAPPED_SYMBOLS_H_ - -// This header overrides the __wrap_X symbols when using the link-time -// -Wl,-wrap,malloc shim-layer approach (see README.md). -// All references to malloc, free, etc. within the linker unit that gets the -// -wrap linker flags (e.g., libchrome.so) will be rewritten to the -// linker as references to __wrap_malloc, __wrap_free, which are defined here. - -#include <algorithm> -#include <cstring> - -#include "base/allocator/partition_allocator/shim/allocator_shim_internals.h" - -extern "C" { - -SHIM_ALWAYS_EXPORT void* __wrap_calloc(size_t n, size_t size) { - return ShimCalloc(n, size, nullptr); -} - -SHIM_ALWAYS_EXPORT void __wrap_free(void* ptr) { - ShimFree(ptr, nullptr); -} - -SHIM_ALWAYS_EXPORT void* __wrap_malloc(size_t size) { - return ShimMalloc(size, nullptr); -} - -SHIM_ALWAYS_EXPORT void* __wrap_memalign(size_t align, size_t size) { - return ShimMemalign(align, size, nullptr); -} - -SHIM_ALWAYS_EXPORT int __wrap_posix_memalign(void** res, - size_t align, - size_t size) { - return ShimPosixMemalign(res, align, size); -} - -SHIM_ALWAYS_EXPORT void* __wrap_pvalloc(size_t size) { - return ShimPvalloc(size); -} - -SHIM_ALWAYS_EXPORT void* __wrap_realloc(void* address, size_t size) { - return ShimRealloc(address, size, nullptr); -} - -SHIM_ALWAYS_EXPORT void* __wrap_valloc(size_t size) { - return ShimValloc(size, nullptr); -} - -SHIM_ALWAYS_EXPORT size_t __wrap_malloc_usable_size(void* address) { - return ShimGetSizeEstimate(address, nullptr); -} - -const size_t kPathMaxSize = 8192; -static_assert(kPathMaxSize >= PATH_MAX, ""); - -extern char* __wrap_strdup(const char* str); - -// Override <stdlib.h> - -extern char* __real_realpath(const char* path, char* resolved_path); - -SHIM_ALWAYS_EXPORT char* __wrap_realpath(const char* path, - char* resolved_path) { - if (resolved_path) - return __real_realpath(path, resolved_path); - - char buffer[kPathMaxSize]; - if (!__real_realpath(path, buffer)) - return nullptr; - return __wrap_strdup(buffer); -} - -// Override <string.h> functions - -SHIM_ALWAYS_EXPORT char* __wrap_strdup(const char* str) { - std::size_t length = std::strlen(str) + 1; - void* buffer = ShimMalloc(length, nullptr); - if (!buffer) - return nullptr; - return reinterpret_cast<char*>(std::memcpy(buffer, str, length)); -} - -SHIM_ALWAYS_EXPORT char* __wrap_strndup(const char* str, size_t n) { - std::size_t length = std::min(std::strlen(str), n); - char* buffer = reinterpret_cast<char*>(ShimMalloc(length + 1, nullptr)); - if (!buffer) - return nullptr; - std::memcpy(buffer, str, length); - buffer[length] = '\0'; - return buffer; -} - -// Override <unistd.h> - -extern char* __real_getcwd(char* buffer, size_t size); - -SHIM_ALWAYS_EXPORT char* __wrap_getcwd(char* buffer, size_t size) { - if (buffer) - return __real_getcwd(buffer, size); - - if (!size) - size = kPathMaxSize; - char local_buffer[size]; - if (!__real_getcwd(local_buffer, size)) - return nullptr; - return __wrap_strdup(local_buffer); -} - -// Override stdio.h - -// This is non-standard (_GNU_SOURCE only), but implemented by Bionic on -// Android, and used by libc++. -SHIM_ALWAYS_EXPORT int __wrap_vasprintf(char** strp, - const char* fmt, - va_list va_args) { - // There are cases where we need to use the list of arguments twice, namely - // when the original buffer is too small. It is not allowed to walk the list - // twice, so make a copy for the second invocation of vsnprintf(). - va_list va_args_copy; - va_copy(va_args_copy, va_args); - - constexpr int kInitialSize = 128; - *strp = static_cast<char*>( - malloc(kInitialSize)); // Our malloc() doesn't return nullptr. - - int actual_size = vsnprintf(*strp, kInitialSize, fmt, va_args); - if (actual_size < 0) { - va_end(va_args_copy); - return actual_size; - } - *strp = - static_cast<char*>(realloc(*strp, static_cast<size_t>(actual_size + 1))); - - // Now we know the size. This is not very efficient, but we cannot really do - // better without accessing internal libc functions, or reimplementing - // *printf(). - // - // This is very lightly used in Chromium in practice, see crbug.com/116558 for - // details. - if (actual_size >= kInitialSize) { - int ret = vsnprintf(*strp, static_cast<size_t>(actual_size + 1), fmt, - va_args_copy); - va_end(va_args_copy); - return ret; - } - - va_end(va_args_copy); - return actual_size; -} - -SHIM_ALWAYS_EXPORT int __wrap_asprintf(char** strp, const char* fmt, ...) { - va_list va_args; - va_start(va_args, fmt); - int retval = vasprintf(strp, fmt, va_args); - va_end(va_args); - return retval; -} - -} // extern "C" - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_OVERRIDE_LINKER_WRAPPED_SYMBOLS_H_
diff --git a/base/allocator/partition_allocator/shim/allocator_shim_override_mac_default_zone.h b/base/allocator/partition_allocator/shim/allocator_shim_override_mac_default_zone.h deleted file mode 100644 index f096971..0000000 --- a/base/allocator/partition_allocator/shim/allocator_shim_override_mac_default_zone.h +++ /dev/null
@@ -1,411 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifdef BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_OVERRIDE_MAC_DEFAULT_ZONE_H_ -#error This header is meant to be included only once by allocator_shim.cc -#endif - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_OVERRIDE_MAC_DEFAULT_ZONE_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_OVERRIDE_MAC_DEFAULT_ZONE_H_ - -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" - -#if !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -#error This header must be included iff PartitionAlloc-Everywhere is enabled. -#endif - -#include <string.h> - -#include <atomic> -#include <tuple> - -#include "base/allocator/early_zone_registration_mac.h" -#include "base/allocator/partition_allocator/partition_alloc_base/bits.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "base/logging.h" - -namespace partition_alloc { - -// Defined in base/allocator/partition_allocator/partition_root.cc -void PartitionAllocMallocHookOnBeforeForkInParent(); -void PartitionAllocMallocHookOnAfterForkInParent(); -void PartitionAllocMallocHookOnAfterForkInChild(); - -} // namespace partition_alloc - -namespace allocator_shim { - -namespace { - -// malloc_introspection_t's callback functions for our own zone - -kern_return_t MallocIntrospectionEnumerator(task_t task, - void*, - unsigned type_mask, - vm_address_t zone_address, - memory_reader_t reader, - vm_range_recorder_t recorder) { - // Should enumerate all memory regions allocated by this allocator, but not - // implemented just because of no use case for now. - return KERN_FAILURE; -} - -size_t MallocIntrospectionGoodSize(malloc_zone_t* zone, size_t size) { - return partition_alloc::internal::base::bits::AlignUp( - size, partition_alloc::internal::kAlignment); -} - -boolean_t MallocIntrospectionCheck(malloc_zone_t* zone) { - // Should check the consistency of the allocator implementing this malloc - // zone, but not implemented just because of no use case for now. - return true; -} - -void MallocIntrospectionPrint(malloc_zone_t* zone, boolean_t verbose) { - // Should print the current states of the zone for debugging / investigation - // purpose, but not implemented just because of no use case for now. -} - -void MallocIntrospectionLog(malloc_zone_t* zone, void* address) { - // Should enable logging of the activities on the given `address`, but not - // implemented just because of no use case for now. -} - -void MallocIntrospectionForceLock(malloc_zone_t* zone) { - // Called before fork(2) to acquire the lock. - partition_alloc::PartitionAllocMallocHookOnBeforeForkInParent(); -} - -void MallocIntrospectionForceUnlock(malloc_zone_t* zone) { - // Called in the parent process after fork(2) to release the lock. - partition_alloc::PartitionAllocMallocHookOnAfterForkInParent(); -} - -void MallocIntrospectionStatistics(malloc_zone_t* zone, - malloc_statistics_t* stats) { - // Should report the memory usage correctly, but not implemented just because - // of no use case for now. - stats->blocks_in_use = 0; - stats->size_in_use = 0; - stats->max_size_in_use = 0; // High water mark of touched memory - stats->size_allocated = 0; // Reserved in memory -} - -boolean_t MallocIntrospectionZoneLocked(malloc_zone_t* zone) { - // Should return true if the underlying PartitionRoot is locked, but not - // implemented just because this function seems not used effectively. - return false; -} - -boolean_t MallocIntrospectionEnableDischargeChecking(malloc_zone_t* zone) { - // 'discharge' is not supported. - return false; -} - -void MallocIntrospectionDisableDischargeChecking(malloc_zone_t* zone) { - // 'discharge' is not supported. -} - -void MallocIntrospectionDischarge(malloc_zone_t* zone, void* memory) { - // 'discharge' is not supported. -} - -void MallocIntrospectionEnumerateDischargedPointers( - malloc_zone_t* zone, - void (^report_discharged)(void* memory, void* info)) { - // 'discharge' is not supported. -} - -void MallocIntrospectionReinitLock(malloc_zone_t* zone) { - // Called in a child process after fork(2) to re-initialize the lock. - partition_alloc::PartitionAllocMallocHookOnAfterForkInChild(); -} - -void MallocIntrospectionPrintTask(task_t task, - unsigned level, - vm_address_t zone_address, - memory_reader_t reader, - print_task_printer_t printer) { - // Should print the current states of another process's zone for debugging / - // investigation purpose, but not implemented just because of no use case - // for now. -} - -void MallocIntrospectionTaskStatistics(task_t task, - vm_address_t zone_address, - memory_reader_t reader, - malloc_statistics_t* stats) { - // Should report the memory usage in another process's zone, but not - // implemented just because of no use case for now. - stats->blocks_in_use = 0; - stats->size_in_use = 0; - stats->max_size_in_use = 0; // High water mark of touched memory - stats->size_allocated = 0; // Reserved in memory -} - -// malloc_zone_t's callback functions for our own zone - -size_t MallocZoneSize(malloc_zone_t* zone, const void* ptr) { - return ShimGetSizeEstimate(ptr, nullptr); -} - -void* MallocZoneMalloc(malloc_zone_t* zone, size_t size) { - return ShimMalloc(size, nullptr); -} - -void* MallocZoneCalloc(malloc_zone_t* zone, size_t n, size_t size) { - return ShimCalloc(n, size, nullptr); -} - -void* MallocZoneValloc(malloc_zone_t* zone, size_t size) { - return ShimValloc(size, nullptr); -} - -void MallocZoneFree(malloc_zone_t* zone, void* ptr) { - return ShimFree(ptr, nullptr); -} - -void* MallocZoneRealloc(malloc_zone_t* zone, void* ptr, size_t size) { - return ShimRealloc(ptr, size, nullptr); -} - -void MallocZoneDestroy(malloc_zone_t* zone) { - // No support to destroy the zone for now. -} - -void* MallocZoneMemalign(malloc_zone_t* zone, size_t alignment, size_t size) { - return ShimMemalign(alignment, size, nullptr); -} - -void MallocZoneFreeDefiniteSize(malloc_zone_t* zone, void* ptr, size_t size) { - return ShimFreeDefiniteSize(ptr, size, nullptr); -} - -unsigned MallocZoneBatchMalloc(malloc_zone_t* zone, - size_t size, - void** results, - unsigned num_requested) { - return ShimBatchMalloc(size, results, num_requested, nullptr); -} - -void MallocZoneBatchFree(malloc_zone_t* zone, - void** to_be_freed, - unsigned num) { - return ShimBatchFree(to_be_freed, num, nullptr); -} - -boolean_t MallocZoneClaimedAddress(malloc_zone_t* zone, void* ptr) { - return static_cast<boolean_t>(ShimClaimedAddress(ptr, nullptr)); -} - -#if PA_TRY_FREE_DEFAULT_IS_AVAILABLE -void MallocZoneTryFreeDefault(malloc_zone_t* zone, void* ptr) { - return ShimTryFreeDefault(ptr, nullptr); -} -#endif - -malloc_introspection_t g_mac_malloc_introspection{}; -malloc_zone_t g_mac_malloc_zone{}; - -malloc_zone_t* GetDefaultMallocZone() { - // malloc_default_zone() does not return... the default zone, but the initial - // one. The default one is the first element of the default zone array. - unsigned int zone_count = 0; - vm_address_t* zones = nullptr; - kern_return_t result = - malloc_get_all_zones(mach_task_self(), nullptr, &zones, &zone_count); - MACH_CHECK(result == KERN_SUCCESS, result) << "malloc_get_all_zones"; - return reinterpret_cast<malloc_zone_t*>(zones[0]); -} - -bool IsAlreadyRegistered() { - // HACK: This should really only be called once, but it is not. - // - // This function is a static constructor of its binary. If it is included in a - // dynamic library, then the same process may end up executing this code - // multiple times, once per library. As a consequence, each new library will - // add its own allocator as the default zone. Aside from splitting the heap - // further, the main issue arises if/when the last library to be loaded - // (dlopen()-ed) gets dlclose()-ed. - // - // See crbug.com/1271139 for details. - // - // In this case, subsequent free() will be routed by libmalloc to the deleted - // zone (since its code has been unloaded from memory), and crash inside - // libsystem's free(). This in practice happens as soon as dlclose() is - // called, inside the dynamic linker (dyld). - // - // Since we are talking about different library, and issues inside the dynamic - // linker, we cannot use a global static variable (which would be - // per-library), or anything from pthread. - // - // The solution used here is to check whether the current default zone is - // already ours, in which case we are not the first dynamic library here, and - // should do nothing. This is racy, and hacky. - vm_address_t* zones = nullptr; - unsigned int zone_count = 0; - // *Not* using malloc_default_zone(), as it seems to be hardcoded to return - // something else than the default zone. See the difference between - // malloc_default_zone() and inline_malloc_default_zone() in Apple's malloc.c - // (in libmalloc). - kern_return_t result = - malloc_get_all_zones(mach_task_self(), nullptr, &zones, &zone_count); - MACH_CHECK(result == KERN_SUCCESS, result) << "malloc_get_all_zones"; - // Checking all the zones, in case someone registered their own zone on top of - // our own. - for (unsigned int i = 0; i < zone_count; i++) { - malloc_zone_t* zone = reinterpret_cast<malloc_zone_t*>(zones[i]); - - // strcmp() and not a pointer comparison, as the zone was registered from - // another library, the pointers don't match. - if (zone->zone_name && - (strcmp(zone->zone_name, partition_alloc::kPartitionAllocZoneName) == - 0)) { - // This zone is provided by PartitionAlloc, so this function has been - // called from another library (or the main executable), nothing to do. - // - // This should be a crash, ideally, but callers do it, so only warn, for - // now. - RAW_LOG(ERROR, - "Trying to load the allocator multiple times. This is *not* " - "supported."); - return true; - } - } - - return false; -} - -void InitializeZone() { - g_mac_malloc_introspection.enumerator = MallocIntrospectionEnumerator; - g_mac_malloc_introspection.good_size = MallocIntrospectionGoodSize; - g_mac_malloc_introspection.check = MallocIntrospectionCheck; - g_mac_malloc_introspection.print = MallocIntrospectionPrint; - g_mac_malloc_introspection.log = MallocIntrospectionLog; - g_mac_malloc_introspection.force_lock = MallocIntrospectionForceLock; - g_mac_malloc_introspection.force_unlock = MallocIntrospectionForceUnlock; - g_mac_malloc_introspection.statistics = MallocIntrospectionStatistics; - g_mac_malloc_introspection.zone_locked = MallocIntrospectionZoneLocked; - g_mac_malloc_introspection.enable_discharge_checking = - MallocIntrospectionEnableDischargeChecking; - g_mac_malloc_introspection.disable_discharge_checking = - MallocIntrospectionDisableDischargeChecking; - g_mac_malloc_introspection.discharge = MallocIntrospectionDischarge; - g_mac_malloc_introspection.enumerate_discharged_pointers = - MallocIntrospectionEnumerateDischargedPointers; - g_mac_malloc_introspection.reinit_lock = MallocIntrospectionReinitLock; - g_mac_malloc_introspection.print_task = MallocIntrospectionPrintTask; - g_mac_malloc_introspection.task_statistics = - MallocIntrospectionTaskStatistics; - // `version` member indicates which APIs are supported in this zone. - // version >= 5: memalign is supported - // version >= 6: free_definite_size is supported - // version >= 7: introspect's discharge family is supported - // version >= 8: pressure_relief is supported - // version >= 9: introspect.reinit_lock is supported - // version >= 10: claimed_address is supported - // version >= 11: introspect.print_task is supported - // version >= 12: introspect.task_statistics is supported - // version >= 13: try_free_default is supported - g_mac_malloc_zone.version = partition_alloc::kZoneVersion; - g_mac_malloc_zone.zone_name = partition_alloc::kPartitionAllocZoneName; - g_mac_malloc_zone.introspect = &g_mac_malloc_introspection; - g_mac_malloc_zone.size = MallocZoneSize; - g_mac_malloc_zone.malloc = MallocZoneMalloc; - g_mac_malloc_zone.calloc = MallocZoneCalloc; - g_mac_malloc_zone.valloc = MallocZoneValloc; - g_mac_malloc_zone.free = MallocZoneFree; - g_mac_malloc_zone.realloc = MallocZoneRealloc; - g_mac_malloc_zone.destroy = MallocZoneDestroy; - g_mac_malloc_zone.batch_malloc = MallocZoneBatchMalloc; - g_mac_malloc_zone.batch_free = MallocZoneBatchFree; - g_mac_malloc_zone.memalign = MallocZoneMemalign; - g_mac_malloc_zone.free_definite_size = MallocZoneFreeDefiniteSize; - g_mac_malloc_zone.pressure_relief = nullptr; - g_mac_malloc_zone.claimed_address = MallocZoneClaimedAddress; -#if PA_TRY_FREE_DEFAULT_IS_AVAILABLE - g_mac_malloc_zone.try_free_default = MallocZoneTryFreeDefault; -#endif -} - -namespace { -static std::atomic<bool> g_initialization_is_done; -} - -// Replaces the default malloc zone with our own malloc zone backed by -// PartitionAlloc. Since we'd like to make as much code as possible to use our -// own memory allocator (and reduce bugs caused by mixed use of the system -// allocator and our own allocator), run the following function -// `InitializeDefaultAllocatorPartitionRoot` with the highest priority. -// -// Note that, despite of the highest priority of the initialization order, -// [NSThread init] runs before InitializeDefaultMallocZoneWithPartitionAlloc -// unfortunately and allocates memory with the system allocator. Plus, the -// allocated memory will be deallocated with the default zone's `free` at that -// moment without using a zone dispatcher. Hence, our own `free` function -// receives an address allocated by the system allocator. -__attribute__((constructor(0))) void -InitializeDefaultMallocZoneWithPartitionAlloc() { - if (IsAlreadyRegistered()) - return; - - // Instantiate the existing regular and purgeable zones in order to make the - // existing purgeable zone use the existing regular zone since PartitionAlloc - // doesn't support a purgeable zone. - std::ignore = malloc_default_zone(); - std::ignore = malloc_default_purgeable_zone(); - - // Initialize the default allocator's PartitionRoot with the existing zone. - InitializeDefaultAllocatorPartitionRoot(); - - // Create our own malloc zone. - InitializeZone(); - - malloc_zone_t* system_default_zone = GetDefaultMallocZone(); - if (strcmp(system_default_zone->zone_name, - partition_alloc::kDelegatingZoneName) == 0) { - // The first zone is our zone, we can unregister it, replacing it with the - // new one. This relies on a precise zone setup, done in - // |EarlyMallocZoneRegistration()|. - malloc_zone_register(&g_mac_malloc_zone); - malloc_zone_unregister(system_default_zone); - g_initialization_is_done.store(true, std::memory_order_release); - return; - } - - // Not in the path where the zone was registered early. This is either racy, - // or fine if the current process is not hosting multiple threads. - // - // This path is fine for e.g. most unit tests. - // - // Make our own zone the default zone. - // - // Put our own zone at the last position, so that it promotes to the default - // zone. The implementation logic of malloc_zone_unregister is: - // zone_table.swap(unregistered_zone, last_zone); - // zone_table.shrink_size_by_1(); - malloc_zone_register(&g_mac_malloc_zone); - malloc_zone_unregister(system_default_zone); - // Between malloc_zone_unregister(system_default_zone) (above) and - // malloc_zone_register(system_default_zone) (below), i.e. while absence of - // system_default_zone, it's possible that another thread calls free(ptr) and - // "no zone found" error is hit, crashing the process. - malloc_zone_register(system_default_zone); - - // Confirm that our own zone is now the default zone. - CHECK_EQ(GetDefaultMallocZone(), &g_mac_malloc_zone); - g_initialization_is_done.store(true, std::memory_order_release); -} - -} // namespace - -bool IsDefaultAllocatorPartitionRootInitialized() { - // Even though zone registration is not thread-safe, let's not make it worse, - // and use acquire/release ordering. - return g_initialization_is_done.load(std::memory_order_acquire); -} - -} // namespace allocator_shim - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_OVERRIDE_MAC_DEFAULT_ZONE_H_
diff --git a/base/allocator/partition_allocator/shim/allocator_shim_override_mac_symbols.h b/base/allocator/partition_allocator/shim/allocator_shim_override_mac_symbols.h deleted file mode 100644 index 08e76ab..0000000 --- a/base/allocator/partition_allocator/shim/allocator_shim_override_mac_symbols.h +++ /dev/null
@@ -1,69 +0,0 @@ -// Copyright 2017 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifdef BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_OVERRIDE_MAC_SYMBOLS_H_ -#error This header is meant to be included only once by allocator_shim.cc -#endif - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_OVERRIDE_MAC_SYMBOLS_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_OVERRIDE_MAC_SYMBOLS_H_ - -#include "base/allocator/partition_allocator/shim/malloc_zone_functions_mac.h" -#include "third_party/apple_apsl/malloc.h" - -namespace allocator_shim { - -MallocZoneFunctions MallocZoneFunctionsToReplaceDefault() { - MallocZoneFunctions new_functions; - memset(&new_functions, 0, sizeof(MallocZoneFunctions)); - new_functions.size = [](malloc_zone_t* zone, const void* ptr) -> size_t { - return ShimGetSizeEstimate(ptr, zone); - }; - new_functions.claimed_address = [](malloc_zone_t* zone, - void* ptr) -> boolean_t { - return ShimClaimedAddress(ptr, zone); - }; - new_functions.malloc = [](malloc_zone_t* zone, size_t size) -> void* { - return ShimMalloc(size, zone); - }; - new_functions.calloc = [](malloc_zone_t* zone, size_t n, - size_t size) -> void* { - return ShimCalloc(n, size, zone); - }; - new_functions.valloc = [](malloc_zone_t* zone, size_t size) -> void* { - return ShimValloc(size, zone); - }; - new_functions.free = [](malloc_zone_t* zone, void* ptr) { - ShimFree(ptr, zone); - }; - new_functions.realloc = [](malloc_zone_t* zone, void* ptr, - size_t size) -> void* { - return ShimRealloc(ptr, size, zone); - }; - new_functions.batch_malloc = [](struct _malloc_zone_t* zone, size_t size, - void** results, - unsigned num_requested) -> unsigned { - return ShimBatchMalloc(size, results, num_requested, zone); - }; - new_functions.batch_free = [](struct _malloc_zone_t* zone, void** to_be_freed, - unsigned num_to_be_freed) -> void { - ShimBatchFree(to_be_freed, num_to_be_freed, zone); - }; - new_functions.memalign = [](malloc_zone_t* zone, size_t alignment, - size_t size) -> void* { - return ShimMemalign(alignment, size, zone); - }; - new_functions.free_definite_size = [](malloc_zone_t* zone, void* ptr, - size_t size) { - ShimFreeDefiniteSize(ptr, size, zone); - }; - new_functions.try_free_default = [](malloc_zone_t* zone, void* ptr) { - ShimTryFreeDefault(ptr, zone); - }; - return new_functions; -} - -} // namespace allocator_shim - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_OVERRIDE_MAC_SYMBOLS_H_
diff --git a/base/allocator/partition_allocator/shim/allocator_shim_override_ucrt_symbols_win.h b/base/allocator/partition_allocator/shim/allocator_shim_override_ucrt_symbols_win.h deleted file mode 100644 index ed377e9..0000000 --- a/base/allocator/partition_allocator/shim/allocator_shim_override_ucrt_symbols_win.h +++ /dev/null
@@ -1,182 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This header defines symbols to override the same functions in the Visual C++ -// CRT implementation. - -#ifdef BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_OVERRIDE_UCRT_SYMBOLS_WIN_H_ -#error This header is meant to be included only once by allocator_shim.cc -#endif - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_OVERRIDE_UCRT_SYMBOLS_WIN_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_OVERRIDE_UCRT_SYMBOLS_WIN_H_ - -#include <malloc.h> - -#include <windows.h> - -#include "base/allocator/partition_allocator/shim/allocator_shim_internals.h" - -// Even though most C++ allocation operators can be left alone since the -// interception works at a lower level, these ones should be -// overridden. Otherwise they redirect to malloc(), which is configured to crash -// with an OOM in failure cases, such as allocation requests that are too large. -SHIM_ALWAYS_EXPORT void* operator new(size_t size, - const std::nothrow_t&) noexcept { - return ShimCppNewNoThrow(size); -} - -SHIM_ALWAYS_EXPORT void* operator new[](size_t size, - const std::nothrow_t&) noexcept { - return ShimCppNewNoThrow(size); -} - -extern "C" { - -void* (*malloc_unchecked)(size_t) = &allocator_shim::UncheckedAlloc; - -namespace { - -int win_new_mode = 0; - -} // namespace - -// This function behaves similarly to MSVC's _set_new_mode. -// If flag is 0 (default), calls to malloc will behave normally. -// If flag is 1, calls to malloc will behave like calls to new, -// and the std_new_handler will be invoked on failure. -// Returns the previous mode. -// -// Replaces _set_new_mode in ucrt\heap\new_mode.cpp -int _set_new_mode(int flag) { - // The MS CRT calls this function early on in startup, so this serves as a low - // overhead proof that the allocator shim is in place for this process. - allocator_shim::g_is_win_shim_layer_initialized = true; - int old_mode = win_new_mode; - win_new_mode = flag; - - allocator_shim::SetCallNewHandlerOnMallocFailure(win_new_mode != 0); - - return old_mode; -} - -// Replaces _query_new_mode in ucrt\heap\new_mode.cpp -int _query_new_mode() { - return win_new_mode; -} - -// These symbols override the CRT's implementation of the same functions. -__declspec(restrict) void* malloc(size_t size) { - return ShimMalloc(size, nullptr); -} - -void free(void* ptr) { - ShimFree(ptr, nullptr); -} - -__declspec(restrict) void* realloc(void* ptr, size_t size) { - return ShimRealloc(ptr, size, nullptr); -} - -__declspec(restrict) void* calloc(size_t n, size_t size) { - return ShimCalloc(n, size, nullptr); -} - -// _msize() is the Windows equivalent of malloc_size(). -size_t _msize(void* memblock) { - return ShimGetSizeEstimate(memblock, nullptr); -} - -__declspec(restrict) void* _aligned_malloc(size_t size, size_t alignment) { - return ShimAlignedMalloc(size, alignment, nullptr); -} - -__declspec(restrict) void* _aligned_realloc(void* address, - size_t size, - size_t alignment) { - return ShimAlignedRealloc(address, size, alignment, nullptr); -} - -void _aligned_free(void* address) { - ShimAlignedFree(address, nullptr); -} - -// _recalloc_base is called by CRT internally. -__declspec(restrict) void* _recalloc_base(void* block, - size_t count, - size_t size) { - const size_t old_block_size = (block != nullptr) ? _msize(block) : 0; - base::CheckedNumeric<size_t> new_block_size_checked = count; - new_block_size_checked *= size; - const size_t new_block_size = new_block_size_checked.ValueOrDie(); - - void* const new_block = realloc(block, new_block_size); - - if (new_block != nullptr && old_block_size < new_block_size) { - memset(static_cast<char*>(new_block) + old_block_size, 0, - new_block_size - old_block_size); - } - - return new_block; -} - -__declspec(restrict) void* _malloc_base(size_t size) { - return malloc(size); -} - -__declspec(restrict) void* _calloc_base(size_t n, size_t size) { - return calloc(n, size); -} - -void _free_base(void* block) { - free(block); -} - -__declspec(restrict) void* _recalloc(void* block, size_t count, size_t size) { - return _recalloc_base(block, count, size); -} - -// The following uncommon _aligned_* routines are not used in Chromium and have -// been shimmed to immediately crash to ensure that implementations are added if -// uses are introduced. -__declspec(restrict) void* _aligned_recalloc(void* address, - size_t num, - size_t size, - size_t alignment) { - CHECK(false) << "This routine has not been implemented"; - __builtin_unreachable(); -} - -size_t _aligned_msize(void* address, size_t alignment, size_t offset) { - CHECK(false) << "This routine has not been implemented"; - __builtin_unreachable(); -} - -__declspec(restrict) void* _aligned_offset_malloc(size_t size, - size_t alignment, - size_t offset) { - CHECK(false) << "This routine has not been implemented"; - __builtin_unreachable(); -} - -__declspec(restrict) void* _aligned_offset_realloc(void* address, - size_t size, - size_t alignment, - size_t offset) { - CHECK(false) << "This routine has not been implemented"; - __builtin_unreachable(); -} - -__declspec(restrict) void* _aligned_offset_recalloc(void* address, - size_t num, - size_t size, - size_t alignment, - size_t offset) { - CHECK(false) << "This routine has not been implemented"; - __builtin_unreachable(); -} - -} // extern "C" - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_ALLOCATOR_SHIM_OVERRIDE_UCRT_SYMBOLS_WIN_H_
diff --git a/base/allocator/partition_allocator/shim/allocator_shim_unittest.cc b/base/allocator/partition_allocator/shim/allocator_shim_unittest.cc deleted file mode 100644 index a8539e1..0000000 --- a/base/allocator/partition_allocator/shim/allocator_shim_unittest.cc +++ /dev/null
@@ -1,778 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/shim/allocator_shim.h" - -#include <stdlib.h> -#include <string.h> - -#include <atomic> -#include <iomanip> -#include <memory> -#include <new> -#include <sstream> -#include <vector> - -#include "base/allocator/partition_allocator/partition_alloc.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/memory/page_size.h" -#include "base/synchronization/waitable_event.h" -#include "base/threading/platform_thread.h" -#include "build/build_config.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -#if BUILDFLAG(IS_WIN) -#include <malloc.h> -#include <windows.h> -#elif BUILDFLAG(IS_APPLE) -#include <malloc/malloc.h> - -#include "base/allocator/partition_allocator/shim/allocator_interception_mac.h" -#include "third_party/apple_apsl/malloc.h" -#else -#include <malloc.h> -#endif - -#if !BUILDFLAG(IS_WIN) -#include <unistd.h> -#endif - -#if defined(LIBC_GLIBC) -extern "C" void* __libc_memalign(size_t align, size_t s); -#endif - -namespace allocator_shim { -namespace { - -using testing::_; -using testing::MockFunction; - -// Special sentinel values used for testing GetSizeEstimate() interception. -const char kTestSizeEstimateData[] = "test_value"; -constexpr void* kTestSizeEstimateAddress = (void*)kTestSizeEstimateData; -constexpr size_t kTestSizeEstimate = 1234; - -class AllocatorShimTest : public testing::Test { - public: - AllocatorShimTest() : testing::Test() {} - - static size_t Hash(const void* ptr) { - return reinterpret_cast<uintptr_t>(ptr) % MaxSizeTracked(); - } - - static void* MockAlloc(const AllocatorDispatch* self, - size_t size, - void* context) { - if (instance_ && size < MaxSizeTracked()) - ++(instance_->allocs_intercepted_by_size[size]); - return self->next->alloc_function(self->next, size, context); - } - - static void* MockAllocUnchecked(const AllocatorDispatch* self, - size_t size, - void* context) { - if (instance_ && size < MaxSizeTracked()) - ++(instance_->allocs_intercepted_by_size[size]); - return self->next->alloc_unchecked_function(self->next, size, context); - } - - static void* MockAllocZeroInit(const AllocatorDispatch* self, - size_t n, - size_t size, - void* context) { - const size_t real_size = n * size; - if (instance_ && real_size < MaxSizeTracked()) - ++(instance_->zero_allocs_intercepted_by_size[real_size]); - return self->next->alloc_zero_initialized_function(self->next, n, size, - context); - } - - static void* MockAllocAligned(const AllocatorDispatch* self, - size_t alignment, - size_t size, - void* context) { - if (instance_) { - if (size < MaxSizeTracked()) - ++(instance_->aligned_allocs_intercepted_by_size[size]); - if (alignment < MaxSizeTracked()) - ++(instance_->aligned_allocs_intercepted_by_alignment[alignment]); - } - return self->next->alloc_aligned_function(self->next, alignment, size, - context); - } - - static void* MockRealloc(const AllocatorDispatch* self, - void* address, - size_t size, - void* context) { - if (instance_) { - // Size 0xFEED is a special sentinel for the NewHandlerConcurrency test. - // Hitting it for the first time will cause a failure, causing the - // invocation of the std::new_handler. - if (size == 0xFEED) { - thread_local bool did_fail_realloc_0xfeed_once = false; - if (!did_fail_realloc_0xfeed_once) { - did_fail_realloc_0xfeed_once = true; - return nullptr; - } - return address; - } - - if (size < MaxSizeTracked()) - ++(instance_->reallocs_intercepted_by_size[size]); - ++instance_->reallocs_intercepted_by_addr[Hash(address)]; - } - return self->next->realloc_function(self->next, address, size, context); - } - - static void MockFree(const AllocatorDispatch* self, - void* address, - void* context) { - if (instance_) { - ++instance_->frees_intercepted_by_addr[Hash(address)]; - } - self->next->free_function(self->next, address, context); - } - - static size_t MockGetSizeEstimate(const AllocatorDispatch* self, - void* address, - void* context) { - // Special testing values for GetSizeEstimate() interception. - if (address == kTestSizeEstimateAddress) - return kTestSizeEstimate; - return self->next->get_size_estimate_function(self->next, address, context); - } - - static bool MockClaimedAddress(const AllocatorDispatch* self, - void* address, - void* context) { - // The same as MockGetSizeEstimate. - if (address == kTestSizeEstimateAddress) - return true; - return self->next->claimed_address_function(self->next, address, context); - } - - static unsigned MockBatchMalloc(const AllocatorDispatch* self, - size_t size, - void** results, - unsigned num_requested, - void* context) { - if (instance_) { - instance_->batch_mallocs_intercepted_by_size[size] = - instance_->batch_mallocs_intercepted_by_size[size] + num_requested; - } - return self->next->batch_malloc_function(self->next, size, results, - num_requested, context); - } - - static void MockBatchFree(const AllocatorDispatch* self, - void** to_be_freed, - unsigned num_to_be_freed, - void* context) { - if (instance_) { - for (unsigned i = 0; i < num_to_be_freed; ++i) { - ++instance_->batch_frees_intercepted_by_addr[Hash(to_be_freed[i])]; - } - } - self->next->batch_free_function(self->next, to_be_freed, num_to_be_freed, - context); - } - - static void MockFreeDefiniteSize(const AllocatorDispatch* self, - void* ptr, - size_t size, - void* context) { - if (instance_) { - ++instance_->frees_intercepted_by_addr[Hash(ptr)]; - ++instance_->free_definite_sizes_intercepted_by_size[size]; - } - self->next->free_definite_size_function(self->next, ptr, size, context); - } - - static void MockTryFreeDefault(const AllocatorDispatch* self, - void* ptr, - void* context) { - if (instance_) { - ++instance_->frees_intercepted_by_addr[Hash(ptr)]; - } - self->next->try_free_default_function(self->next, ptr, context); - } - - static void* MockAlignedMalloc(const AllocatorDispatch* self, - size_t size, - size_t alignment, - void* context) { - if (instance_ && size < MaxSizeTracked()) { - ++instance_->aligned_mallocs_intercepted_by_size[size]; - } - return self->next->aligned_malloc_function(self->next, size, alignment, - context); - } - - static void* MockAlignedRealloc(const AllocatorDispatch* self, - void* address, - size_t size, - size_t alignment, - void* context) { - if (instance_) { - if (size < MaxSizeTracked()) - ++instance_->aligned_reallocs_intercepted_by_size[size]; - ++instance_->aligned_reallocs_intercepted_by_addr[Hash(address)]; - } - return self->next->aligned_realloc_function(self->next, address, size, - alignment, context); - } - - static void MockAlignedFree(const AllocatorDispatch* self, - void* address, - void* context) { - if (instance_) { - ++instance_->aligned_frees_intercepted_by_addr[Hash(address)]; - } - self->next->aligned_free_function(self->next, address, context); - } - - static void NewHandler() { - if (!instance_) - return; - instance_->num_new_handler_calls.fetch_add(1, std::memory_order_relaxed); - } - - int32_t GetNumberOfNewHandlerCalls() { - return instance_->num_new_handler_calls.load(std::memory_order_acquire); - } - - void SetUp() override { - allocs_intercepted_by_size.resize(MaxSizeTracked()); - zero_allocs_intercepted_by_size.resize(MaxSizeTracked()); - aligned_allocs_intercepted_by_size.resize(MaxSizeTracked()); - aligned_allocs_intercepted_by_alignment.resize(MaxSizeTracked()); - reallocs_intercepted_by_size.resize(MaxSizeTracked()); - reallocs_intercepted_by_addr.resize(MaxSizeTracked()); - frees_intercepted_by_addr.resize(MaxSizeTracked()); - batch_mallocs_intercepted_by_size.resize(MaxSizeTracked()); - batch_frees_intercepted_by_addr.resize(MaxSizeTracked()); - free_definite_sizes_intercepted_by_size.resize(MaxSizeTracked()); - aligned_mallocs_intercepted_by_size.resize(MaxSizeTracked()); - aligned_reallocs_intercepted_by_size.resize(MaxSizeTracked()); - aligned_reallocs_intercepted_by_addr.resize(MaxSizeTracked()); - aligned_frees_intercepted_by_addr.resize(MaxSizeTracked()); - num_new_handler_calls.store(0, std::memory_order_release); - instance_ = this; - -#if BUILDFLAG(IS_APPLE) - InitializeAllocatorShim(); -#endif - } - - void TearDown() override { - instance_ = nullptr; -#if BUILDFLAG(IS_APPLE) - UninterceptMallocZonesForTesting(); -#endif - } - - static size_t MaxSizeTracked() { -#if BUILDFLAG(IS_IOS) - // TODO(crbug.com/1077271): 64-bit iOS uses a page size that is larger than - // SystemPageSize(), causing this test to make larger allocations, relative - // to SystemPageSize(). - return 6 * partition_alloc::internal::SystemPageSize(); -#else - return 2 * partition_alloc::internal::SystemPageSize(); -#endif - } - - protected: - std::vector<size_t> allocs_intercepted_by_size; - std::vector<size_t> zero_allocs_intercepted_by_size; - std::vector<size_t> aligned_allocs_intercepted_by_size; - std::vector<size_t> aligned_allocs_intercepted_by_alignment; - std::vector<size_t> reallocs_intercepted_by_size; - std::vector<size_t> reallocs_intercepted_by_addr; - std::vector<size_t> frees_intercepted_by_addr; - std::vector<size_t> batch_mallocs_intercepted_by_size; - std::vector<size_t> batch_frees_intercepted_by_addr; - std::vector<size_t> free_definite_sizes_intercepted_by_size; - std::vector<size_t> aligned_mallocs_intercepted_by_size; - std::vector<size_t> aligned_reallocs_intercepted_by_size; - std::vector<size_t> aligned_reallocs_intercepted_by_addr; - std::vector<size_t> aligned_frees_intercepted_by_addr; - std::atomic<uint32_t> num_new_handler_calls; - - private: - static AllocatorShimTest* instance_; -}; - -struct TestStruct1 { - uint32_t ignored; - uint8_t ignored_2; -}; - -struct TestStruct2 { - uint64_t ignored; - uint8_t ignored_3; -}; - -class ThreadDelegateForNewHandlerTest : public base::PlatformThread::Delegate { - public: - explicit ThreadDelegateForNewHandlerTest(base::WaitableEvent* event) - : event_(event) {} - - void ThreadMain() override { - event_->Wait(); - void* temp = malloc(1); - void* res = realloc(temp, 0xFEED); - EXPECT_EQ(temp, res); - } - - private: - base::WaitableEvent* event_; -}; - -AllocatorShimTest* AllocatorShimTest::instance_ = nullptr; - -AllocatorDispatch g_mock_dispatch = { - &AllocatorShimTest::MockAlloc, /* alloc_function */ - &AllocatorShimTest::MockAllocUnchecked, /* alloc_unchecked_function */ - &AllocatorShimTest::MockAllocZeroInit, /* alloc_zero_initialized_function */ - &AllocatorShimTest::MockAllocAligned, /* alloc_aligned_function */ - &AllocatorShimTest::MockRealloc, /* realloc_function */ - &AllocatorShimTest::MockFree, /* free_function */ - &AllocatorShimTest::MockGetSizeEstimate, /* get_size_estimate_function */ - &AllocatorShimTest::MockClaimedAddress, /* claimed_address_function */ - &AllocatorShimTest::MockBatchMalloc, /* batch_malloc_function */ - &AllocatorShimTest::MockBatchFree, /* batch_free_function */ - &AllocatorShimTest::MockFreeDefiniteSize, /* free_definite_size_function */ - &AllocatorShimTest::MockTryFreeDefault, /* try_free_default_function */ - &AllocatorShimTest::MockAlignedMalloc, /* aligned_malloc_function */ - &AllocatorShimTest::MockAlignedRealloc, /* aligned_realloc_function */ - &AllocatorShimTest::MockAlignedFree, /* aligned_free_function */ - nullptr, /* next */ -}; - -TEST_F(AllocatorShimTest, InterceptLibcSymbols) { - InsertAllocatorDispatch(&g_mock_dispatch); - - void* alloc_ptr = malloc(19); - ASSERT_NE(nullptr, alloc_ptr); - ASSERT_GE(allocs_intercepted_by_size[19], 1u); - - void* zero_alloc_ptr = calloc(2, 23); - ASSERT_NE(nullptr, zero_alloc_ptr); - ASSERT_GE(zero_allocs_intercepted_by_size[2 * 23], 1u); - -#if !BUILDFLAG(IS_WIN) - void* posix_memalign_ptr = nullptr; - int res = posix_memalign(&posix_memalign_ptr, 256, 59); - ASSERT_EQ(0, res); - ASSERT_NE(nullptr, posix_memalign_ptr); - ASSERT_EQ(0u, reinterpret_cast<uintptr_t>(posix_memalign_ptr) % 256); - ASSERT_GE(aligned_allocs_intercepted_by_alignment[256], 1u); - ASSERT_GE(aligned_allocs_intercepted_by_size[59], 1u); - - // (p)valloc() are not defined on Android. pvalloc() is a GNU extension, - // valloc() is not in POSIX. -#if !BUILDFLAG(IS_ANDROID) - const size_t kPageSize = base::GetPageSize(); - void* valloc_ptr = valloc(61); - ASSERT_NE(nullptr, valloc_ptr); - ASSERT_EQ(0u, reinterpret_cast<uintptr_t>(valloc_ptr) % kPageSize); - ASSERT_GE(aligned_allocs_intercepted_by_alignment[kPageSize], 1u); - ASSERT_GE(aligned_allocs_intercepted_by_size[61], 1u); -#endif // !BUILDFLAG(IS_ANDROID) - -#endif // !BUILDFLAG(IS_WIN) - -#if !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_APPLE) - void* memalign_ptr = memalign(128, 53); - ASSERT_NE(nullptr, memalign_ptr); - ASSERT_EQ(0u, reinterpret_cast<uintptr_t>(memalign_ptr) % 128); - ASSERT_GE(aligned_allocs_intercepted_by_alignment[128], 1u); - ASSERT_GE(aligned_allocs_intercepted_by_size[53], 1u); - -#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) - void* pvalloc_ptr = pvalloc(67); - ASSERT_NE(nullptr, pvalloc_ptr); - ASSERT_EQ(0u, reinterpret_cast<uintptr_t>(pvalloc_ptr) % kPageSize); - ASSERT_GE(aligned_allocs_intercepted_by_alignment[kPageSize], 1u); - // pvalloc rounds the size up to the next page. - ASSERT_GE(aligned_allocs_intercepted_by_size[kPageSize], 1u); -#endif // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) - -#endif // !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_APPLE) - -// See allocator_shim_override_glibc_weak_symbols.h for why we intercept -// internal libc symbols. -#if defined(LIBC_GLIBC) && BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - void* libc_memalign_ptr = __libc_memalign(512, 56); - ASSERT_NE(nullptr, memalign_ptr); - ASSERT_EQ(0u, reinterpret_cast<uintptr_t>(libc_memalign_ptr) % 512); - ASSERT_GE(aligned_allocs_intercepted_by_alignment[512], 1u); - ASSERT_GE(aligned_allocs_intercepted_by_size[56], 1u); -#endif - - char* realloc_ptr = static_cast<char*>(malloc(10)); - strcpy(realloc_ptr, "foobar"); - void* old_realloc_ptr = realloc_ptr; - realloc_ptr = static_cast<char*>(realloc(realloc_ptr, 73)); - ASSERT_GE(reallocs_intercepted_by_size[73], 1u); - ASSERT_GE(reallocs_intercepted_by_addr[Hash(old_realloc_ptr)], 1u); - ASSERT_EQ(0, strcmp(realloc_ptr, "foobar")); - - free(alloc_ptr); - ASSERT_GE(frees_intercepted_by_addr[Hash(alloc_ptr)], 1u); - - free(zero_alloc_ptr); - ASSERT_GE(frees_intercepted_by_addr[Hash(zero_alloc_ptr)], 1u); - -#if !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_APPLE) - free(memalign_ptr); - ASSERT_GE(frees_intercepted_by_addr[Hash(memalign_ptr)], 1u); - -#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) - free(pvalloc_ptr); - ASSERT_GE(frees_intercepted_by_addr[Hash(pvalloc_ptr)], 1u); -#endif // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) - -#endif // !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_APPLE) - -#if !BUILDFLAG(IS_WIN) - free(posix_memalign_ptr); - ASSERT_GE(frees_intercepted_by_addr[Hash(posix_memalign_ptr)], 1u); - -#if !BUILDFLAG(IS_ANDROID) - free(valloc_ptr); - ASSERT_GE(frees_intercepted_by_addr[Hash(valloc_ptr)], 1u); -#endif // !BUILDFLAG(IS_ANDROID) - -#endif // !BUILDFLAG(IS_WIN) - -#if defined(LIBC_GLIBC) && BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - free(libc_memalign_ptr); - ASSERT_GE(frees_intercepted_by_addr[Hash(memalign_ptr)], 1u); -#endif - - free(realloc_ptr); - ASSERT_GE(frees_intercepted_by_addr[Hash(realloc_ptr)], 1u); - - RemoveAllocatorDispatchForTesting(&g_mock_dispatch); - - void* non_hooked_ptr = malloc(4095); - ASSERT_NE(nullptr, non_hooked_ptr); - ASSERT_EQ(0u, allocs_intercepted_by_size[4095]); - free(non_hooked_ptr); -} - -// PartitionAlloc-Everywhere does not support batch_malloc / batch_free. -#if BUILDFLAG(IS_APPLE) && !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -TEST_F(AllocatorShimTest, InterceptLibcSymbolsBatchMallocFree) { - InsertAllocatorDispatch(&g_mock_dispatch); - - unsigned count = 13; - std::vector<void*> results; - results.resize(count); - unsigned result_count = malloc_zone_batch_malloc(malloc_default_zone(), 99, - results.data(), count); - ASSERT_EQ(count, result_count); - - // TODO(erikchen): On macOS 10.12+, batch_malloc in the default zone may - // forward to another zone, which we've also shimmed, resulting in - // MockBatchMalloc getting called twice as often as we'd expect. This - // re-entrancy into the allocator shim is a bug that needs to be fixed. - // https://crbug.com/693237. - // ASSERT_EQ(count, batch_mallocs_intercepted_by_size[99]); - - std::vector<void*> results_copy(results); - malloc_zone_batch_free(malloc_default_zone(), results.data(), count); - for (void* result : results_copy) { - ASSERT_GE(batch_frees_intercepted_by_addr[Hash(result)], 1u); - } - RemoveAllocatorDispatchForTesting(&g_mock_dispatch); -} - -TEST_F(AllocatorShimTest, InterceptLibcSymbolsFreeDefiniteSize) { - InsertAllocatorDispatch(&g_mock_dispatch); - - void* alloc_ptr = malloc(19); - ASSERT_NE(nullptr, alloc_ptr); - ASSERT_GE(allocs_intercepted_by_size[19], 1u); - - ChromeMallocZone* default_zone = - reinterpret_cast<ChromeMallocZone*>(malloc_default_zone()); - default_zone->free_definite_size(malloc_default_zone(), alloc_ptr, 19); - ASSERT_GE(free_definite_sizes_intercepted_by_size[19], 1u); - RemoveAllocatorDispatchForTesting(&g_mock_dispatch); -} -#endif // BUILDFLAG(IS_APPLE) && !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - -#if BUILDFLAG(IS_WIN) -TEST_F(AllocatorShimTest, InterceptUcrtAlignedAllocationSymbols) { - InsertAllocatorDispatch(&g_mock_dispatch); - - constexpr size_t kAlignment = 32; - void* alloc_ptr = _aligned_malloc(123, kAlignment); - EXPECT_GE(aligned_mallocs_intercepted_by_size[123], 1u); - - void* new_alloc_ptr = _aligned_realloc(alloc_ptr, 1234, kAlignment); - EXPECT_GE(aligned_reallocs_intercepted_by_size[1234], 1u); - EXPECT_GE(aligned_reallocs_intercepted_by_addr[Hash(alloc_ptr)], 1u); - - _aligned_free(new_alloc_ptr); - EXPECT_GE(aligned_frees_intercepted_by_addr[Hash(new_alloc_ptr)], 1u); - - RemoveAllocatorDispatchForTesting(&g_mock_dispatch); -} - -TEST_F(AllocatorShimTest, AlignedReallocSizeZeroFrees) { - void* alloc_ptr = _aligned_malloc(123, 16); - CHECK(alloc_ptr); - alloc_ptr = _aligned_realloc(alloc_ptr, 0, 16); - CHECK(!alloc_ptr); -} -#endif // BUILDFLAG(IS_WIN) - -TEST_F(AllocatorShimTest, InterceptCppSymbols) { - InsertAllocatorDispatch(&g_mock_dispatch); - - TestStruct1* new_ptr = new TestStruct1; - ASSERT_NE(nullptr, new_ptr); - ASSERT_GE(allocs_intercepted_by_size[sizeof(TestStruct1)], 1u); - - TestStruct1* new_array_ptr = new TestStruct1[3]; - ASSERT_NE(nullptr, new_array_ptr); - ASSERT_GE(allocs_intercepted_by_size[sizeof(TestStruct1) * 3], 1u); - - TestStruct2* new_nt_ptr = new (std::nothrow) TestStruct2; - ASSERT_NE(nullptr, new_nt_ptr); - ASSERT_GE(allocs_intercepted_by_size[sizeof(TestStruct2)], 1u); - - TestStruct2* new_array_nt_ptr = new TestStruct2[3]; - ASSERT_NE(nullptr, new_array_nt_ptr); - ASSERT_GE(allocs_intercepted_by_size[sizeof(TestStruct2) * 3], 1u); - - delete new_ptr; - ASSERT_GE(frees_intercepted_by_addr[Hash(new_ptr)], 1u); - - delete[] new_array_ptr; - ASSERT_GE(frees_intercepted_by_addr[Hash(new_array_ptr)], 1u); - - delete new_nt_ptr; - ASSERT_GE(frees_intercepted_by_addr[Hash(new_nt_ptr)], 1u); - - delete[] new_array_nt_ptr; - ASSERT_GE(frees_intercepted_by_addr[Hash(new_array_nt_ptr)], 1u); - - RemoveAllocatorDispatchForTesting(&g_mock_dispatch); -} - -// PartitionAlloc disallows large allocations to avoid errors with int -// overflows. -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -struct TooLarge { - char padding1[1UL << 31]; - int padding2; -}; - -TEST_F(AllocatorShimTest, NewNoThrowTooLarge) { - char* too_large_array = new (std::nothrow) char[(1UL << 31) + 100]; - EXPECT_EQ(nullptr, too_large_array); - - TooLarge* too_large_struct = new (std::nothrow) TooLarge; - EXPECT_EQ(nullptr, too_large_struct); -} -#endif - -// This test exercises the case of concurrent OOM failure, which would end up -// invoking std::new_handler concurrently. This is to cover the CallNewHandler() -// paths of allocator_shim.cc and smoke-test its thread safey. -// The test creates kNumThreads threads. Each of them mallocs some memory, and -// then does a realloc(<new memory>, 0xFEED). -// The shim intercepts such realloc and makes it fail only once on each thread. -// We expect to see excactly kNumThreads invocations of the new_handler. -TEST_F(AllocatorShimTest, NewHandlerConcurrency) { - const int kNumThreads = 32; - base::PlatformThreadHandle threads[kNumThreads]; - - // The WaitableEvent here is used to attempt to trigger all the threads at - // the same time, after they have been initialized. - base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED); - - ThreadDelegateForNewHandlerTest mock_thread_main(&event); - - for (auto& thread : threads) - base::PlatformThread::Create(0, &mock_thread_main, &thread); - - std::set_new_handler(&AllocatorShimTest::NewHandler); - SetCallNewHandlerOnMallocFailure(true); // It's going to fail on realloc(). - InsertAllocatorDispatch(&g_mock_dispatch); - event.Signal(); - for (auto& thread : threads) - base::PlatformThread::Join(thread); - RemoveAllocatorDispatchForTesting(&g_mock_dispatch); - ASSERT_EQ(kNumThreads, GetNumberOfNewHandlerCalls()); -} - -#if BUILDFLAG(IS_WIN) -TEST_F(AllocatorShimTest, ShimReplacesCRTHeapWhenEnabled) { - ASSERT_EQ(::GetProcessHeap(), reinterpret_cast<HANDLE>(_get_heap_handle())); -} -#endif // BUILDFLAG(IS_WIN) - -#if BUILDFLAG(IS_WIN) -static size_t GetUsableSize(void* ptr) { - return _msize(ptr); -} -#elif BUILDFLAG(IS_APPLE) -static size_t GetUsableSize(void* ptr) { - return malloc_size(ptr); -} -#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) -static size_t GetUsableSize(void* ptr) { - return malloc_usable_size(ptr); -} -#else -#define NO_MALLOC_SIZE -#endif - -#if !defined(NO_MALLOC_SIZE) -TEST_F(AllocatorShimTest, ShimReplacesMallocSizeWhenEnabled) { - InsertAllocatorDispatch(&g_mock_dispatch); - EXPECT_EQ(GetUsableSize(kTestSizeEstimateAddress), kTestSizeEstimate); - RemoveAllocatorDispatchForTesting(&g_mock_dispatch); -} - -TEST_F(AllocatorShimTest, ShimDoesntChangeMallocSizeWhenEnabled) { - void* alloc = malloc(16); - size_t sz = GetUsableSize(alloc); - EXPECT_GE(sz, 16U); - - InsertAllocatorDispatch(&g_mock_dispatch); - EXPECT_EQ(GetUsableSize(alloc), sz); - RemoveAllocatorDispatchForTesting(&g_mock_dispatch); - - free(alloc); -} -#endif // !defined(NO_MALLOC_SIZE) - -#if BUILDFLAG(IS_ANDROID) -TEST_F(AllocatorShimTest, InterceptCLibraryFunctions) { - auto total_counts = [](const std::vector<size_t>& counts) { - size_t total = 0; - for (const auto count : counts) - total += count; - return total; - }; - size_t counts_before; - size_t counts_after = total_counts(allocs_intercepted_by_size); - void* ptr; - - InsertAllocatorDispatch(&g_mock_dispatch); - - // <stdlib.h> - counts_before = counts_after; - ptr = realpath(".", nullptr); - EXPECT_NE(nullptr, ptr); - free(ptr); - counts_after = total_counts(allocs_intercepted_by_size); - EXPECT_GT(counts_after, counts_before); - - // <string.h> - counts_before = counts_after; - ptr = strdup("hello, world"); - EXPECT_NE(nullptr, ptr); - free(ptr); - counts_after = total_counts(allocs_intercepted_by_size); - EXPECT_GT(counts_after, counts_before); - - counts_before = counts_after; - ptr = strndup("hello, world", 5); - EXPECT_NE(nullptr, ptr); - free(ptr); - counts_after = total_counts(allocs_intercepted_by_size); - EXPECT_GT(counts_after, counts_before); - - // <unistd.h> - counts_before = counts_after; - ptr = getcwd(nullptr, 0); - EXPECT_NE(nullptr, ptr); - free(ptr); - counts_after = total_counts(allocs_intercepted_by_size); - EXPECT_GT(counts_after, counts_before); - - // With component builds on Android, we cannot intercept calls to functions - // inside another component, in this instance the call to vasprintf() inside - // libc++. This is not necessarily an issue for allocator shims, as long as we - // accept that allocations and deallocations will not be matched at all times. - // It is however essential for PartitionAlloc, which is exercized in the test - // below. -#ifndef COMPONENT_BUILD - // Calls vasprintf() indirectly, see below. - counts_before = counts_after; - std::stringstream stream; - stream << std::setprecision(1) << std::showpoint << std::fixed << 1.e38; - EXPECT_GT(stream.str().size(), 30u); - counts_after = total_counts(allocs_intercepted_by_size); - EXPECT_GT(counts_after, counts_before); -#endif // COMPONENT_BUILD - - RemoveAllocatorDispatchForTesting(&g_mock_dispatch); -} - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -// Non-regression test for crbug.com/1166558. -TEST_F(AllocatorShimTest, InterceptVasprintf) { - // Printing a float which expands to >=30 characters calls vasprintf() in - // libc, which we should intercept. - std::stringstream stream; - stream << std::setprecision(1) << std::showpoint << std::fixed << 1.e38; - EXPECT_GT(stream.str().size(), 30u); - // Should not crash. -} - -TEST_F(AllocatorShimTest, InterceptLongVasprintf) { - char* str = nullptr; - const char* lorem_ipsum = - "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. " - "Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, " - "ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula " - "massa, varius a, semper congue, euismod non, mi. Proin porttitor, orci " - "nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit " - "amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat " - "in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero " - "pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo " - "in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue " - "blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus " - "et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed " - "pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales " - "hendrerit."; - int err = asprintf(&str, "%s", lorem_ipsum); - EXPECT_EQ(err, static_cast<int>(strlen(lorem_ipsum))); - EXPECT_TRUE(str); - free(str); -} - -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - -#endif // BUILDFLAG(IS_ANDROID) - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && BUILDFLAG(IS_APPLE) - -// Non-regression test for crbug.com/1291885. -TEST_F(AllocatorShimTest, BatchMalloc) { - constexpr unsigned kNumToAllocate = 20; - void* pointers[kNumToAllocate]; - - EXPECT_EQ(kNumToAllocate, malloc_zone_batch_malloc(malloc_default_zone(), 10, - pointers, kNumToAllocate)); - malloc_zone_batch_free(malloc_default_zone(), pointers, kNumToAllocate); - // Should not crash. -} - -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && BUILDFLAG(IS_APPLE) - -} // namespace -} // namespace allocator_shim
diff --git a/base/allocator/partition_allocator/shim/malloc_zone_functions_mac.cc b/base/allocator/partition_allocator/shim/malloc_zone_functions_mac.cc deleted file mode 100644 index 5c69325..0000000 --- a/base/allocator/partition_allocator/shim/malloc_zone_functions_mac.cc +++ /dev/null
@@ -1,124 +0,0 @@ -// Copyright 2017 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/shim/malloc_zone_functions_mac.h" - -#include <atomic> -#include <type_traits> - -#include "base/synchronization/lock.h" - -namespace allocator_shim { - -MallocZoneFunctions g_malloc_zones[kMaxZoneCount]; -static_assert(std::is_pod<MallocZoneFunctions>::value, - "MallocZoneFunctions must be POD"); - -void StoreZoneFunctions(const ChromeMallocZone* zone, - MallocZoneFunctions* functions) { - memset(functions, 0, sizeof(MallocZoneFunctions)); - functions->malloc = zone->malloc; - functions->calloc = zone->calloc; - functions->valloc = zone->valloc; - functions->free = zone->free; - functions->realloc = zone->realloc; - functions->size = zone->size; - CHECK(functions->malloc && functions->calloc && functions->valloc && - functions->free && functions->realloc && functions->size); - - // These functions might be nullptr. - functions->batch_malloc = zone->batch_malloc; - functions->batch_free = zone->batch_free; - - if (zone->version >= 5) { - // Not all custom malloc zones have a memalign. - functions->memalign = zone->memalign; - } - if (zone->version >= 6) { - // This may be nullptr. - functions->free_definite_size = zone->free_definite_size; - } - if (zone->version >= 10) { - functions->claimed_address = zone->claimed_address; - } - if (zone->version >= 13) { - functions->try_free_default = zone->try_free_default; - } - - // Note that zone version 8 introduced a pressure relief callback, and version - // 10 introduced a claimed address callback, but neither are allocation or - // deallocation callbacks and so aren't important to intercept. - - functions->context = zone; -} - -namespace { - -// All modifications to g_malloc_zones are gated behind this lock. -// Dispatch to a malloc zone does not need to acquire this lock. -base::Lock& GetLock() { - static base::Lock* g_lock = new base::Lock; - return *g_lock; -} - -void EnsureMallocZonesInitializedLocked() { - GetLock().AssertAcquired(); -} - -int g_zone_count = 0; - -bool IsMallocZoneAlreadyStoredLocked(ChromeMallocZone* zone) { - EnsureMallocZonesInitializedLocked(); - GetLock().AssertAcquired(); - for (int i = 0; i < g_zone_count; ++i) { - if (g_malloc_zones[i].context == reinterpret_cast<void*>(zone)) - return true; - } - return false; -} - -} // namespace - -bool StoreMallocZone(ChromeMallocZone* zone) { - base::AutoLock l(GetLock()); - EnsureMallocZonesInitializedLocked(); - if (IsMallocZoneAlreadyStoredLocked(zone)) - return false; - - if (g_zone_count == kMaxZoneCount) - return false; - - StoreZoneFunctions(zone, &g_malloc_zones[g_zone_count]); - ++g_zone_count; - - // No other thread can possibly see these stores at this point. The code that - // reads these values is triggered after this function returns. so we want to - // guarantee that they are committed at this stage" - std::atomic_thread_fence(std::memory_order_seq_cst); - return true; -} - -bool IsMallocZoneAlreadyStored(ChromeMallocZone* zone) { - base::AutoLock l(GetLock()); - return IsMallocZoneAlreadyStoredLocked(zone); -} - -bool DoesMallocZoneNeedReplacing(ChromeMallocZone* zone, - const MallocZoneFunctions* functions) { - return IsMallocZoneAlreadyStored(zone) && zone->malloc != functions->malloc; -} - -int GetMallocZoneCountForTesting() { - base::AutoLock l(GetLock()); - return g_zone_count; -} - -void ClearAllMallocZonesForTesting() { - base::AutoLock l(GetLock()); - EnsureMallocZonesInitializedLocked(); - memset(g_malloc_zones, 0, kMaxZoneCount * sizeof(MallocZoneFunctions)); - g_zone_count = 0; -} - -} // namespace allocator_shim
diff --git a/base/allocator/partition_allocator/shim/malloc_zone_functions_mac.h b/base/allocator/partition_allocator/shim/malloc_zone_functions_mac.h deleted file mode 100644 index 984f3d5..0000000 --- a/base/allocator/partition_allocator/shim/malloc_zone_functions_mac.h +++ /dev/null
@@ -1,106 +0,0 @@ -// Copyright 2017 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_MALLOC_ZONE_FUNCTIONS_MAC_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_MALLOC_ZONE_FUNCTIONS_MAC_H_ - -#include <malloc/malloc.h> -#include <stddef.h> - -#include "base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h" -#include "base/base_export.h" -#include "third_party/apple_apsl/malloc.h" - -namespace allocator_shim { - -typedef void* (*malloc_type)(struct _malloc_zone_t* zone, size_t size); -typedef void* (*calloc_type)(struct _malloc_zone_t* zone, - size_t num_items, - size_t size); -typedef void* (*valloc_type)(struct _malloc_zone_t* zone, size_t size); -typedef void (*free_type)(struct _malloc_zone_t* zone, void* ptr); -typedef void* (*realloc_type)(struct _malloc_zone_t* zone, - void* ptr, - size_t size); -typedef void* (*memalign_type)(struct _malloc_zone_t* zone, - size_t alignment, - size_t size); -typedef unsigned (*batch_malloc_type)(struct _malloc_zone_t* zone, - size_t size, - void** results, - unsigned num_requested); -typedef void (*batch_free_type)(struct _malloc_zone_t* zone, - void** to_be_freed, - unsigned num_to_be_freed); -typedef void (*free_definite_size_type)(struct _malloc_zone_t* zone, - void* ptr, - size_t size); -typedef void (*try_free_default_type)(struct _malloc_zone_t* zone, void* ptr); -typedef size_t (*size_fn_type)(struct _malloc_zone_t* zone, const void* ptr); -typedef boolean_t (*claimed_address_type)(struct _malloc_zone_t* zone, - void* ptr); - -struct MallocZoneFunctions { - malloc_type malloc; - calloc_type calloc; - valloc_type valloc; - free_type free; - realloc_type realloc; - memalign_type memalign; - batch_malloc_type batch_malloc; - batch_free_type batch_free; - free_definite_size_type free_definite_size; - try_free_default_type try_free_default; - size_fn_type size; - claimed_address_type claimed_address; - const ChromeMallocZone* context; -}; - -BASE_EXPORT void StoreZoneFunctions(const ChromeMallocZone* zone, - MallocZoneFunctions* functions); -static constexpr int kMaxZoneCount = 30; -BASE_EXPORT extern MallocZoneFunctions g_malloc_zones[kMaxZoneCount]; - -// The array g_malloc_zones stores all information about malloc zones before -// they are shimmed. This information needs to be accessed during dispatch back -// into the zone, and additional zones may be added later in the execution fo -// the program, so the array needs to be both thread-safe and high-performance. -// -// We begin by creating an array of MallocZoneFunctions of fixed size. We will -// never modify the container, which provides thread-safety to iterators. When -// we want to add a MallocZoneFunctions to the container, we: -// 1. Fill in all the fields. -// 2. Update the total zone count. -// 3. Insert a memory barrier. -// 4. Insert our shim. -// -// Each MallocZoneFunctions is uniquely identified by |context|, which is a -// pointer to the original malloc zone. When we wish to dispatch back to the -// original malloc zones, we iterate through the array, looking for a matching -// |context|. -// -// Most allocations go through the default allocator. We will ensure that the -// default allocator is stored as the first MallocZoneFunctions. -// -// Returns whether the zone was successfully stored. -BASE_EXPORT bool StoreMallocZone(ChromeMallocZone* zone); -BASE_EXPORT bool IsMallocZoneAlreadyStored(ChromeMallocZone* zone); -BASE_EXPORT bool DoesMallocZoneNeedReplacing( - ChromeMallocZone* zone, - const MallocZoneFunctions* functions); - -BASE_EXPORT int GetMallocZoneCountForTesting(); -BASE_EXPORT void ClearAllMallocZonesForTesting(); - -inline MallocZoneFunctions& GetFunctionsForZone(void* zone) { - for (unsigned int i = 0; i < kMaxZoneCount; ++i) { - if (g_malloc_zones[i].context == zone) - return g_malloc_zones[i]; - } - PA_IMMEDIATE_CRASH(); -} - -} // namespace allocator_shim - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_SHIM_MALLOC_ZONE_FUNCTIONS_MAC_H_
diff --git a/base/allocator/partition_allocator/shim/malloc_zone_functions_mac_unittest.cc b/base/allocator/partition_allocator/shim/malloc_zone_functions_mac_unittest.cc deleted file mode 100644 index a5e61b5..0000000 --- a/base/allocator/partition_allocator/shim/malloc_zone_functions_mac_unittest.cc +++ /dev/null
@@ -1,56 +0,0 @@ -// Copyright 2017 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/shim/malloc_zone_functions_mac.h" - -#include "testing/gtest/include/gtest/gtest.h" - -namespace allocator_shim { - -class MallocZoneFunctionsTest : public testing::Test { - protected: - void TearDown() override { ClearAllMallocZonesForTesting(); } -}; - -TEST_F(MallocZoneFunctionsTest, TestDefaultZoneMallocFree) { - ChromeMallocZone* malloc_zone = - reinterpret_cast<ChromeMallocZone*>(malloc_default_zone()); - StoreMallocZone(malloc_zone); - int* test = reinterpret_cast<int*>( - g_malloc_zones[0].malloc(malloc_default_zone(), 33)); - test[0] = 1; - test[1] = 2; - g_malloc_zones[0].free(malloc_default_zone(), test); -} - -TEST_F(MallocZoneFunctionsTest, IsZoneAlreadyStored) { - ChromeMallocZone* malloc_zone = - reinterpret_cast<ChromeMallocZone*>(malloc_default_zone()); - EXPECT_FALSE(IsMallocZoneAlreadyStored(malloc_zone)); - StoreMallocZone(malloc_zone); - EXPECT_TRUE(IsMallocZoneAlreadyStored(malloc_zone)); -} - -TEST_F(MallocZoneFunctionsTest, CannotDoubleStoreZone) { - ChromeMallocZone* malloc_zone = - reinterpret_cast<ChromeMallocZone*>(malloc_default_zone()); - StoreMallocZone(malloc_zone); - StoreMallocZone(malloc_zone); - EXPECT_EQ(1, GetMallocZoneCountForTesting()); -} - -TEST_F(MallocZoneFunctionsTest, CannotStoreMoreThanMaxZones) { - std::vector<ChromeMallocZone> zones; - zones.resize(kMaxZoneCount * 2); - for (int i = 0; i < kMaxZoneCount * 2; ++i) { - ChromeMallocZone& zone = zones[i]; - memcpy(&zone, malloc_default_zone(), sizeof(ChromeMallocZone)); - StoreMallocZone(&zone); - } - - int max_zone_count = kMaxZoneCount; - EXPECT_EQ(max_zone_count, GetMallocZoneCountForTesting()); -} - -} // namespace allocator_shim
diff --git a/base/allocator/partition_allocator/shim/winheap_stubs_win.cc b/base/allocator/partition_allocator/shim/winheap_stubs_win.cc deleted file mode 100644 index eb8a456..0000000 --- a/base/allocator/partition_allocator/shim/winheap_stubs_win.cc +++ /dev/null
@@ -1,209 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This code should move into the default Windows shim once the win-specific -// allocation shim has been removed, and the generic shim has becaome the -// default. - -#include "winheap_stubs_win.h" - -#include <limits.h> -#include <malloc.h> -#include <new.h> -#include <windows.h> -#include <algorithm> -#include <limits> - -#include "base/allocator/partition_allocator/partition_alloc_base/bits.h" -#include "base/allocator/partition_allocator/partition_alloc_base/numerics/safe_conversions.h" -#include "base/allocator/partition_allocator/partition_alloc_check.h" - -namespace allocator_shim { - -bool g_is_win_shim_layer_initialized = false; - -namespace { - -const size_t kWindowsPageSize = 4096; -const size_t kMaxWindowsAllocation = INT_MAX - kWindowsPageSize; - -inline HANDLE get_heap_handle() { - return reinterpret_cast<HANDLE>(_get_heap_handle()); -} - -} // namespace - -void* WinHeapMalloc(size_t size) { - if (size < kMaxWindowsAllocation) - return HeapAlloc(get_heap_handle(), 0, size); - return nullptr; -} - -void WinHeapFree(void* ptr) { - if (!ptr) - return; - - HeapFree(get_heap_handle(), 0, ptr); -} - -void* WinHeapRealloc(void* ptr, size_t size) { - if (!ptr) - return WinHeapMalloc(size); - if (!size) { - WinHeapFree(ptr); - return nullptr; - } - if (size < kMaxWindowsAllocation) - return HeapReAlloc(get_heap_handle(), 0, ptr, size); - return nullptr; -} - -size_t WinHeapGetSizeEstimate(void* ptr) { - if (!ptr) - return 0; - - return HeapSize(get_heap_handle(), 0, ptr); -} - -// Call the new handler, if one has been set. -// Returns true on successfully calling the handler, false otherwise. -bool WinCallNewHandler(size_t size) { -#ifdef _CPPUNWIND -#error "Exceptions in allocator shim are not supported!" -#endif // _CPPUNWIND - // Get the current new handler. - _PNH nh = _query_new_handler(); - if (!nh) - return false; - // Since exceptions are disabled, we don't really know if new_handler - // failed. Assume it will abort if it fails. - return nh(size) ? true : false; -} - -// The Windows _aligned_* functions are implemented by creating an allocation -// with enough space to create an aligned allocation internally. The offset to -// the original allocation is prefixed to the aligned allocation so that it can -// be correctly freed. - -namespace { - -struct AlignedPrefix { - // Offset to the original allocation point. - unsigned int original_allocation_offset; - // Make sure an unsigned int is enough to store the offset - static_assert( - kMaxWindowsAllocation < std::numeric_limits<unsigned int>::max(), - "original_allocation_offset must be able to fit into an unsigned int"); -#if BUILDFLAG(PA_DCHECK_IS_ON) - // Magic value used to check that _aligned_free() and _aligned_realloc() are - // only ever called on an aligned allocated chunk. - static constexpr unsigned int kMagic = 0x12003400; - unsigned int magic; -#endif // BUILDFLAG(PA_DCHECK_IS_ON) -}; - -// Compute how large an allocation we need to fit an allocation with the given -// size and alignment and space for a prefix pointer. -size_t AdjustedSize(size_t size, size_t alignment) { - // Minimal alignment is the prefix size so the prefix is properly aligned. - alignment = std::max(alignment, alignof(AlignedPrefix)); - return size + sizeof(AlignedPrefix) + alignment - 1; -} - -// Align the allocation and write the prefix. -void* AlignAllocation(void* ptr, size_t alignment) { - // Minimal alignment is the prefix size so the prefix is properly aligned. - alignment = std::max(alignment, alignof(AlignedPrefix)); - - uintptr_t address = reinterpret_cast<uintptr_t>(ptr); - address = partition_alloc::internal::base::bits::AlignUp( - address + sizeof(AlignedPrefix), alignment); - - // Write the prefix. - AlignedPrefix* prefix = reinterpret_cast<AlignedPrefix*>(address) - 1; - prefix->original_allocation_offset = - partition_alloc::internal::base::checked_cast<unsigned int>( - address - reinterpret_cast<uintptr_t>(ptr)); -#if BUILDFLAG(PA_DCHECK_IS_ON) - prefix->magic = AlignedPrefix::kMagic; -#endif // BUILDFLAG(PA_DCHECK_IS_ON) - return reinterpret_cast<void*>(address); -} - -// Return the original allocation from an aligned allocation. -void* UnalignAllocation(void* ptr) { - AlignedPrefix* prefix = reinterpret_cast<AlignedPrefix*>(ptr) - 1; -#if BUILDFLAG(PA_DCHECK_IS_ON) - PA_DCHECK(prefix->magic == AlignedPrefix::kMagic); -#endif // BUILDFLAG(PA_DCHECK_IS_ON) - void* unaligned = - static_cast<uint8_t*>(ptr) - prefix->original_allocation_offset; - PA_CHECK(unaligned < ptr); - PA_CHECK(reinterpret_cast<uintptr_t>(ptr) - - reinterpret_cast<uintptr_t>(unaligned) <= - kMaxWindowsAllocation); - return unaligned; -} - -} // namespace - -void* WinHeapAlignedMalloc(size_t size, size_t alignment) { - PA_CHECK(partition_alloc::internal::base::bits::IsPowerOfTwo(alignment)); - - size_t adjusted = AdjustedSize(size, alignment); - if (adjusted >= kMaxWindowsAllocation) - return nullptr; - - void* ptr = WinHeapMalloc(adjusted); - if (!ptr) - return nullptr; - - return AlignAllocation(ptr, alignment); -} - -void* WinHeapAlignedRealloc(void* ptr, size_t size, size_t alignment) { - PA_CHECK(partition_alloc::internal::base::bits::IsPowerOfTwo(alignment)); - - if (!ptr) - return WinHeapAlignedMalloc(size, alignment); - if (!size) { - WinHeapAlignedFree(ptr); - return nullptr; - } - - size_t adjusted = AdjustedSize(size, alignment); - if (adjusted >= kMaxWindowsAllocation) - return nullptr; - - // Try to resize the allocation in place first. - void* unaligned = UnalignAllocation(ptr); - if (HeapReAlloc(get_heap_h