mirror of
https://github.com/girlbossceo/hardened_malloc-rs.git
synced 2025-04-29 14:19:25 -04:00
submodule stuff
Signed-off-by: strawberry <june@girlboss.ceo>
This commit is contained in:
parent
c8de762782
commit
2a8abbdef5
86 changed files with 2 additions and 9884 deletions
2
.gitmodules
vendored
2
.gitmodules
vendored
|
@ -1,3 +1,3 @@
|
|||
[submodule "hardened_malloc"]
|
||||
path = "src/hardened_malloc"
|
||||
path = src/hardened_malloc
|
||||
url = https://github.com/GrapheneOS/hardened_malloc.git
|
||||
|
|
1
src/hardened_malloc
Submodule
1
src/hardened_malloc
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 4171bd164e2ec4cf2546daa2b0f6f95af0d782df
|
|
@ -1,2 +0,0 @@
|
|||
Checks: 'bugprone-*,-bugprone-easily-swappable-parameters,-bugprone-macro-parentheses,-bugprone-too-small-loop-variable,cert-*,-cert-err33-c,clang-analyzer-*,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,-clang-diagnostic-constant-logical-operand,readability-*,-readability-function-cognitive-complexity,-readability-identifier-length,-readability-inconsistent-declaration-parameter-name,-readability-magic-numbers,-readability-named-parameter,llvm-include-order,misc-*'
|
||||
WarningsAsErrors: '*'
|
7
src/hardened_malloc/.github/dependabot.yml
vendored
7
src/hardened_malloc/.github/dependabot.yml
vendored
|
@ -1,7 +0,0 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
target-branch: main
|
|
@ -1,53 +0,0 @@
|
|||
name: Build and run tests
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
schedule:
|
||||
- cron: '0 2 * * *'
|
||||
|
||||
jobs:
|
||||
build-ubuntu-gcc:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
version: [12]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setting up gcc version
|
||||
run: |
|
||||
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-${{ matrix.version }} 100
|
||||
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-${{ matrix.version }} 100
|
||||
- name: Build
|
||||
run: make test
|
||||
build-ubuntu-clang:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
version: [14, 15]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setting up clang version
|
||||
run: |
|
||||
sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-${{ matrix.version }} 100
|
||||
sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-${{ matrix.version }} 100
|
||||
- name: Build
|
||||
run: CC=clang CXX=clang++ make test
|
||||
build-musl:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: alpine:latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install dependencies
|
||||
run: apk update && apk add build-base python3
|
||||
- name: Build
|
||||
run: make test
|
||||
build-ubuntu-gcc-aarch64:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install dependencies
|
||||
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends gcc-aarch64-linux-gnu g++-aarch64-linux-gnu libgcc-s1-arm64-cross cpp-aarch64-linux-gnu
|
||||
- name: Build
|
||||
run: CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-gcc++ make CONFIG_NATIVE=false
|
2
src/hardened_malloc/.gitignore
vendored
2
src/hardened_malloc/.gitignore
vendored
|
@ -1,2 +0,0 @@
|
|||
out/
|
||||
out-light/
|
|
@ -1,83 +0,0 @@
|
|||
common_cflags = [
|
||||
"-pipe",
|
||||
"-O3",
|
||||
//"-flto",
|
||||
"-fPIC",
|
||||
"-fvisibility=hidden",
|
||||
//"-fno-plt",
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Wcast-align",
|
||||
"-Wcast-qual",
|
||||
"-Wwrite-strings",
|
||||
"-Werror",
|
||||
"-DH_MALLOC_PREFIX",
|
||||
"-DZERO_ON_FREE=true",
|
||||
"-DWRITE_AFTER_FREE_CHECK=true",
|
||||
"-DSLOT_RANDOMIZE=true",
|
||||
"-DSLAB_CANARY=true",
|
||||
"-DSLAB_QUARANTINE_RANDOM_LENGTH=1",
|
||||
"-DSLAB_QUARANTINE_QUEUE_LENGTH=1",
|
||||
"-DCONFIG_EXTENDED_SIZE_CLASSES=true",
|
||||
"-DCONFIG_LARGE_SIZE_CLASSES=true",
|
||||
"-DGUARD_SLABS_INTERVAL=1",
|
||||
"-DGUARD_SIZE_DIVISOR=2",
|
||||
"-DREGION_QUARANTINE_RANDOM_LENGTH=256",
|
||||
"-DREGION_QUARANTINE_QUEUE_LENGTH=1024",
|
||||
"-DREGION_QUARANTINE_SKIP_THRESHOLD=33554432", // 32MiB
|
||||
"-DFREE_SLABS_QUARANTINE_RANDOM_LENGTH=32",
|
||||
"-DCONFIG_CLASS_REGION_SIZE=34359738368", // 32GiB
|
||||
"-DN_ARENA=1",
|
||||
"-DCONFIG_STATS=true",
|
||||
"-DCONFIG_SELF_INIT=false",
|
||||
]
|
||||
|
||||
cc_defaults {
|
||||
name: "hardened_malloc_defaults",
|
||||
defaults: ["linux_bionic_supported"],
|
||||
cflags: common_cflags,
|
||||
conlyflags: ["-std=c17", "-Wmissing-prototypes"],
|
||||
stl: "none",
|
||||
}
|
||||
|
||||
lib_src_files = [
|
||||
"chacha.c",
|
||||
"h_malloc.c",
|
||||
"memory.c",
|
||||
"pages.c",
|
||||
"random.c",
|
||||
"util.c",
|
||||
]
|
||||
|
||||
cc_library {
|
||||
name: "libhardened_malloc",
|
||||
ramdisk_available: true,
|
||||
vendor_ramdisk_available: true,
|
||||
recovery_available: true,
|
||||
defaults: ["hardened_malloc_defaults"],
|
||||
srcs: lib_src_files,
|
||||
export_include_dirs: ["include"],
|
||||
static_libs: ["libasync_safe"],
|
||||
target: {
|
||||
android: {
|
||||
shared: {
|
||||
enabled: false,
|
||||
},
|
||||
system_shared_libs: [],
|
||||
},
|
||||
linux_bionic: {
|
||||
system_shared_libs: [],
|
||||
},
|
||||
},
|
||||
product_variables: {
|
||||
debuggable: {
|
||||
cflags: ["-DLABEL_MEMORY"],
|
||||
},
|
||||
device_has_arm_mte: {
|
||||
cflags: ["-DHAS_ARM_MTE", "-march=armv9-a+memtag"]
|
||||
},
|
||||
},
|
||||
apex_available: [
|
||||
"com.android.runtime",
|
||||
],
|
||||
}
|
|
@ -1,283 +0,0 @@
|
|||
chacha.c is a simple conversion of chacha-merged.c to a keystream-only implementation:
|
||||
|
||||
chacha-merged.c version 20080118
|
||||
D. J. Bernstein
|
||||
Public domain.
|
||||
|
||||
h_malloc.c open-addressed hash table (regions_grow, regions_insert, regions_find, regions_delete):
|
||||
|
||||
Copyright (c) 2008, 2010, 2011, 2016 Otto Moerbeek <otto@drijf.net>
|
||||
Copyright (c) 2012 Matthew Dempsky <matthew@openbsd.org>
|
||||
Copyright (c) 2008 Damien Miller <djm@openbsd.org>
|
||||
Copyright (c) 2000 Poul-Henning Kamp <phk@FreeBSD.org>
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
libdivide:
|
||||
|
||||
Copyright (C) 2010 - 2019 ridiculous_fish, <libdivide@ridiculousfish.com>
|
||||
Copyright (C) 2016 - 2019 Kim Walisch, <kim.walisch@gmail.com>
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
random.c get_random_{type}_uniform functions are based on Fast Random Integer
|
||||
Generation in an Interval by Daniel Lemire
|
||||
|
||||
arm_mte.h arm_mte_tag_and_clear_mem function contents were copied from storeTags function in scudo:
|
||||
|
||||
==============================================================================
|
||||
The LLVM Project is under the Apache License v2.0 with LLVM Exceptions:
|
||||
==============================================================================
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
|
||||
|
||||
---- LLVM Exceptions to the Apache 2.0 License ----
|
||||
|
||||
As an exception, if, as a result of your compiling your source code, portions
|
||||
of this Software are embedded into an Object form of such source code, you
|
||||
may redistribute such embedded portions in such Object form without complying
|
||||
with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
|
||||
|
||||
In addition, if you combine or link compiled forms of this Software with
|
||||
software that is licensed under the GPLv2 ("Combined Software") and if a
|
||||
court of competent jurisdiction determines that the patent provision (Section
|
||||
3), the indemnity provision (Section 9) or other Section of the License
|
||||
conflicts with the conditions of the GPLv2, you may retroactively and
|
||||
prospectively choose to deem waived or otherwise exclude such Section(s) of
|
||||
the License, but only in their entirety and only with respect to the Combined
|
||||
Software.
|
||||
|
||||
==============================================================================
|
|
@ -1,35 +0,0 @@
|
|||
Very important and should be an easy sell:
|
||||
|
||||
* improved robustness for high vma count on high memory machines
|
||||
* much higher `vm.max_map_count` by default
|
||||
* work on improving performance and resource usage with high vma count
|
||||
* add a way to disable the brk heap and have mmap grow upwards like it did in
|
||||
the past (preserving the same high base entropy)
|
||||
|
||||
Somewhat important and an easy sell:
|
||||
|
||||
* alternative to `RLIMIT_AS` for accountable mappings only
|
||||
* memory control groups are sometimes a better option but there are still
|
||||
users of `RLIMIT_AS` that are problematic for mitigations or simply fast
|
||||
garbage collector implementations, etc. mapping lots of `PROT_NONE` memory
|
||||
* mremap flag to disable unmapping the source mapping
|
||||
* also needed by jemalloc for different reasons
|
||||
* not needed if the kernel gets first class support for arbitrarily sized
|
||||
guard pages and a virtual memory quarantine feature
|
||||
* `MREMAP_DONTUNMAP` is now available but doesn't support expanding the
|
||||
mapping which may be an issue due to VMA merging being unreliable
|
||||
|
||||
Fairly infeasible to land but could reduce overhead and extend coverage of
|
||||
security features to other code directly using mmap:
|
||||
|
||||
* first class support for arbitrarily sized guard pages for mmap and mremap to
|
||||
eliminate half of the resulting VMAs and reduce 2 system calls to 1
|
||||
* not usable if it doesn't support mremap (shrink, grow, grow via move)
|
||||
* not usable if the guard page size is static
|
||||
* should support changing guard size for mremap growth via move
|
||||
* must be possible to set it up from the process
|
||||
* virtual memory quarantine
|
||||
* must be possible to set it up from the process
|
||||
* first-class support for aligned mappings with mmap and ideally mremap
|
||||
* not usable unless guard page support is provided and of course it has to
|
||||
work with this too
|
|
@ -1,19 +0,0 @@
|
|||
Copyright © 2018-2023 GrapheneOS
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -1,148 +0,0 @@
|
|||
VARIANT := default
|
||||
|
||||
ifneq ($(VARIANT),)
|
||||
CONFIG_FILE := config/$(VARIANT).mk
|
||||
include config/$(VARIANT).mk
|
||||
endif
|
||||
|
||||
ifeq ($(VARIANT),default)
|
||||
SUFFIX :=
|
||||
else
|
||||
SUFFIX := -$(VARIANT)
|
||||
endif
|
||||
|
||||
OUT := out$(SUFFIX)
|
||||
|
||||
define safe_flag
|
||||
$(shell $(CC) $(if $(filter clang%,$(CC)),-Werror=unknown-warning-option) -E $1 - </dev/null >/dev/null 2>&1 && echo $1 || echo $2)
|
||||
endef
|
||||
|
||||
CPPFLAGS := $(CPPFLAGS) -D_GNU_SOURCE -I include
|
||||
SHARED_FLAGS := -pipe -O3 -flto -fPIC -fvisibility=hidden -fno-plt \
|
||||
$(call safe_flag,-fstack-clash-protection) $(call safe_flag,-fcf-protection) -fstack-protector-strong \
|
||||
-Wall -Wextra $(call safe_flag,-Wcast-align=strict,-Wcast-align) -Wcast-qual -Wwrite-strings \
|
||||
-Wundef
|
||||
|
||||
ifeq ($(CONFIG_WERROR),true)
|
||||
SHARED_FLAGS += -Werror
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_NATIVE),true)
|
||||
SHARED_FLAGS += -march=native
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_UBSAN),true)
|
||||
SHARED_FLAGS += -fsanitize=undefined -fno-sanitize-recover=undefined
|
||||
endif
|
||||
|
||||
CFLAGS := $(CFLAGS) -std=c17 $(SHARED_FLAGS) -Wmissing-prototypes -Wstrict-prototypes
|
||||
CXXFLAGS := $(CXXFLAGS) -std=c++17 -fsized-deallocation $(SHARED_FLAGS)
|
||||
LDFLAGS := $(LDFLAGS) -Wl,-O1,--as-needed,-z,defs,-z,relro,-z,now,-z,nodlopen,-z,text
|
||||
|
||||
SOURCES := chacha.c h_malloc.c memory.c pages.c random.c util.c
|
||||
OBJECTS := $(SOURCES:.c=.o)
|
||||
|
||||
ifeq ($(CONFIG_CXX_ALLOCATOR),true)
|
||||
# make sure LTO is compatible in case CC and CXX don't match (such as clang and g++)
|
||||
CXX := $(CC)
|
||||
LDLIBS += -lstdc++
|
||||
|
||||
SOURCES += new.cc
|
||||
OBJECTS += new.o
|
||||
endif
|
||||
|
||||
OBJECTS := $(addprefix $(OUT)/,$(OBJECTS))
|
||||
|
||||
ifeq (,$(filter $(CONFIG_SEAL_METADATA),true false))
|
||||
$(error CONFIG_SEAL_METADATA must be true or false)
|
||||
endif
|
||||
|
||||
ifeq (,$(filter $(CONFIG_ZERO_ON_FREE),true false))
|
||||
$(error CONFIG_ZERO_ON_FREE must be true or false)
|
||||
endif
|
||||
|
||||
ifeq (,$(filter $(CONFIG_WRITE_AFTER_FREE_CHECK),true false))
|
||||
$(error CONFIG_WRITE_AFTER_FREE_CHECK must be true or false)
|
||||
endif
|
||||
|
||||
ifeq (,$(filter $(CONFIG_SLOT_RANDOMIZE),true false))
|
||||
$(error CONFIG_SLOT_RANDOMIZE must be true or false)
|
||||
endif
|
||||
|
||||
ifeq (,$(filter $(CONFIG_SLAB_CANARY),true false))
|
||||
$(error CONFIG_SLAB_CANARY must be true or false)
|
||||
endif
|
||||
|
||||
ifeq (,$(filter $(CONFIG_EXTENDED_SIZE_CLASSES),true false))
|
||||
$(error CONFIG_EXTENDED_SIZE_CLASSES must be true or false)
|
||||
endif
|
||||
|
||||
ifeq (,$(filter $(CONFIG_LARGE_SIZE_CLASSES),true false))
|
||||
$(error CONFIG_LARGE_SIZE_CLASSES must be true or false)
|
||||
endif
|
||||
|
||||
ifeq (,$(filter $(CONFIG_STATS),true false))
|
||||
$(error CONFIG_STATS must be true or false)
|
||||
endif
|
||||
|
||||
ifeq (,$(filter $(CONFIG_SELF_INIT),true false))
|
||||
$(error CONFIG_SELF_INIT must be true or false)
|
||||
endif
|
||||
|
||||
CPPFLAGS += \
|
||||
-DCONFIG_SEAL_METADATA=$(CONFIG_SEAL_METADATA) \
|
||||
-DZERO_ON_FREE=$(CONFIG_ZERO_ON_FREE) \
|
||||
-DWRITE_AFTER_FREE_CHECK=$(CONFIG_WRITE_AFTER_FREE_CHECK) \
|
||||
-DSLOT_RANDOMIZE=$(CONFIG_SLOT_RANDOMIZE) \
|
||||
-DSLAB_CANARY=$(CONFIG_SLAB_CANARY) \
|
||||
-DSLAB_QUARANTINE_RANDOM_LENGTH=$(CONFIG_SLAB_QUARANTINE_RANDOM_LENGTH) \
|
||||
-DSLAB_QUARANTINE_QUEUE_LENGTH=$(CONFIG_SLAB_QUARANTINE_QUEUE_LENGTH) \
|
||||
-DCONFIG_EXTENDED_SIZE_CLASSES=$(CONFIG_EXTENDED_SIZE_CLASSES) \
|
||||
-DCONFIG_LARGE_SIZE_CLASSES=$(CONFIG_LARGE_SIZE_CLASSES) \
|
||||
-DGUARD_SLABS_INTERVAL=$(CONFIG_GUARD_SLABS_INTERVAL) \
|
||||
-DGUARD_SIZE_DIVISOR=$(CONFIG_GUARD_SIZE_DIVISOR) \
|
||||
-DREGION_QUARANTINE_RANDOM_LENGTH=$(CONFIG_REGION_QUARANTINE_RANDOM_LENGTH) \
|
||||
-DREGION_QUARANTINE_QUEUE_LENGTH=$(CONFIG_REGION_QUARANTINE_QUEUE_LENGTH) \
|
||||
-DREGION_QUARANTINE_SKIP_THRESHOLD=$(CONFIG_REGION_QUARANTINE_SKIP_THRESHOLD) \
|
||||
-DFREE_SLABS_QUARANTINE_RANDOM_LENGTH=$(CONFIG_FREE_SLABS_QUARANTINE_RANDOM_LENGTH) \
|
||||
-DCONFIG_CLASS_REGION_SIZE=$(CONFIG_CLASS_REGION_SIZE) \
|
||||
-DN_ARENA=$(CONFIG_N_ARENA) \
|
||||
-DCONFIG_STATS=$(CONFIG_STATS) \
|
||||
-DCONFIG_SELF_INIT=$(CONFIG_SELF_INIT)
|
||||
|
||||
$(OUT)/libhardened_malloc$(SUFFIX).so: $(OBJECTS) | $(OUT)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -shared $^ $(LDLIBS) -o $@
|
||||
|
||||
$(OUT):
|
||||
mkdir -p $(OUT)
|
||||
|
||||
$(OUT)/chacha.o: chacha.c chacha.h util.h $(CONFIG_FILE) | $(OUT)
|
||||
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||
$(OUT)/h_malloc.o: h_malloc.c include/h_malloc.h mutex.h memory.h pages.h random.h util.h $(CONFIG_FILE) | $(OUT)
|
||||
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||
$(OUT)/memory.o: memory.c memory.h util.h $(CONFIG_FILE) | $(OUT)
|
||||
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||
$(OUT)/new.o: new.cc include/h_malloc.h util.h $(CONFIG_FILE) | $(OUT)
|
||||
$(COMPILE.cc) $(OUTPUT_OPTION) $<
|
||||
$(OUT)/pages.o: pages.c pages.h memory.h util.h $(CONFIG_FILE) | $(OUT)
|
||||
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||
$(OUT)/random.o: random.c random.h chacha.h util.h $(CONFIG_FILE) | $(OUT)
|
||||
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||
$(OUT)/util.o: util.c util.h $(CONFIG_FILE) | $(OUT)
|
||||
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||
|
||||
check: tidy
|
||||
|
||||
tidy:
|
||||
clang-tidy --extra-arg=-std=c17 $(filter %.c,$(SOURCES)) -- $(CPPFLAGS)
|
||||
clang-tidy --extra-arg=-std=c++17 $(filter %.cc,$(SOURCES)) -- $(CPPFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f $(OUT)/libhardened_malloc.so $(OBJECTS)
|
||||
$(MAKE) -C test/ clean
|
||||
|
||||
test: $(OUT)/libhardened_malloc$(SUFFIX).so
|
||||
$(MAKE) -C test/
|
||||
python3 -m unittest discover --start-directory test/
|
||||
|
||||
.PHONY: check clean tidy test
|
File diff suppressed because it is too large
Load diff
|
@ -1,25 +0,0 @@
|
|||
java_test_host {
|
||||
name: "HMallocTest",
|
||||
srcs: [
|
||||
"src/**/*.java",
|
||||
],
|
||||
|
||||
libs: [
|
||||
"tradefed",
|
||||
"compatibility-tradefed",
|
||||
"compatibility-host-util",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"cts-host-utils",
|
||||
"frameworks-base-hostutils",
|
||||
],
|
||||
|
||||
test_suites: [
|
||||
"general-tests",
|
||||
],
|
||||
|
||||
data_device_bins_64: [
|
||||
"memtag_test",
|
||||
],
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration description="hardened_malloc test">
|
||||
|
||||
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
|
||||
<option name="cleanup" value="true" />
|
||||
<option name="push" value="memtag_test->/data/local/tmp/memtag_test" />
|
||||
</target_preparer>
|
||||
|
||||
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
|
||||
<option name="jar" value="HMallocTest.jar" />
|
||||
</test>
|
||||
|
||||
</configuration>
|
|
@ -1,16 +0,0 @@
|
|||
cc_test {
|
||||
name: "memtag_test",
|
||||
srcs: ["memtag_test.cc"],
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
"-Wextra",
|
||||
"-O0",
|
||||
],
|
||||
|
||||
compile_multilib: "64",
|
||||
|
||||
sanitize: {
|
||||
memtag_heap: true,
|
||||
},
|
||||
}
|
|
@ -1,297 +0,0 @@
|
|||
// needed to uncondionally enable assertions
|
||||
#undef NDEBUG
|
||||
#include <assert.h>
|
||||
#include <malloc.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
using namespace std;
|
||||
|
||||
using u8 = uint8_t;
|
||||
using uptr = uintptr_t;
|
||||
using u64 = uint64_t;
|
||||
|
||||
const size_t DEFAULT_ALLOC_SIZE = 8;
|
||||
const size_t CANARY_SIZE = 8;
|
||||
|
||||
void do_context_switch() {
|
||||
utsname s;
|
||||
uname(&s);
|
||||
}
|
||||
|
||||
u8 get_pointer_tag(void *ptr) {
|
||||
return (((uptr) ptr) >> 56) & 0xf;
|
||||
}
|
||||
|
||||
void *untag_pointer(void *ptr) {
|
||||
const uintptr_t mask = UINTPTR_MAX >> 8;
|
||||
return (void *) ((uintptr_t) ptr & mask);
|
||||
}
|
||||
|
||||
// This test checks that slab slot allocation uses tag that is distint from tags of its neighbors
|
||||
// and from the tag of the previous allocation that used the same slot
|
||||
void tag_distinctness() {
|
||||
// 0 and 15 are reserved
|
||||
const int min_tag = 1;
|
||||
const int max_tag = 14;
|
||||
|
||||
struct SizeClass {
|
||||
int size;
|
||||
int slot_cnt;
|
||||
};
|
||||
|
||||
// values from size_classes[] and size_class_slots[] in h_malloc.c
|
||||
SizeClass size_classes[] = {
|
||||
{ .size = 16, .slot_cnt = 256, },
|
||||
{ .size = 32, .slot_cnt = 128, },
|
||||
// this size class is used by allocations that are made by the addr_tag_map, which breaks
|
||||
// tag distinctess checks
|
||||
// { .size = 48, .slot_cnt = 85, },
|
||||
{ .size = 64, .slot_cnt = 64, },
|
||||
{ .size = 80, .slot_cnt = 51, },
|
||||
{ .size = 96, .slot_cnt = 42, },
|
||||
{ .size = 112, .slot_cnt = 36, },
|
||||
{ .size = 128, .slot_cnt = 64, },
|
||||
{ .size = 160, .slot_cnt = 51, },
|
||||
{ .size = 192, .slot_cnt = 64, },
|
||||
{ .size = 224, .slot_cnt = 54, },
|
||||
{ .size = 10240, .slot_cnt = 6, },
|
||||
{ .size = 20480, .slot_cnt = 1, },
|
||||
};
|
||||
|
||||
int tag_usage[max_tag + 1];
|
||||
|
||||
for (size_t sc_idx = 0; sc_idx < sizeof(size_classes) / sizeof(SizeClass); ++sc_idx) {
|
||||
SizeClass &sc = size_classes[sc_idx];
|
||||
|
||||
const size_t full_alloc_size = sc.size;
|
||||
const size_t alloc_size = full_alloc_size - CANARY_SIZE;
|
||||
|
||||
// "tdc" is short for "tag distinctness check"
|
||||
int left_neighbor_tdc_cnt = 0;
|
||||
int right_neighbor_tdc_cnt = 0;
|
||||
int prev_alloc_tdc_cnt = 0;
|
||||
|
||||
int iter_cnt = 600;
|
||||
|
||||
unordered_map<uptr, u8> addr_tag_map;
|
||||
addr_tag_map.reserve(iter_cnt * sc.slot_cnt);
|
||||
|
||||
u64 seen_tags = 0;
|
||||
|
||||
for (int iter = 0; iter < iter_cnt; ++iter) {
|
||||
uptr allocations[256]; // 256 is max slot count
|
||||
|
||||
for (int i = 0; i < sc.slot_cnt; ++i) {
|
||||
u8 *p = (u8 *) malloc(alloc_size);
|
||||
assert(p);
|
||||
uptr addr = (uptr) untag_pointer(p);
|
||||
u8 tag = get_pointer_tag(p);
|
||||
|
||||
assert(tag >= min_tag && tag <= max_tag);
|
||||
seen_tags |= 1 << tag;
|
||||
++tag_usage[tag];
|
||||
|
||||
// check most recent tags of left and right neighbors
|
||||
|
||||
auto left = addr_tag_map.find(addr - full_alloc_size);
|
||||
if (left != addr_tag_map.end()) {
|
||||
assert(left->second != tag);
|
||||
++left_neighbor_tdc_cnt;
|
||||
}
|
||||
|
||||
auto right = addr_tag_map.find(addr + full_alloc_size);
|
||||
if (right != addr_tag_map.end()) {
|
||||
assert(right->second != tag);
|
||||
++right_neighbor_tdc_cnt;
|
||||
}
|
||||
|
||||
// check previous tag of this slot
|
||||
auto prev = addr_tag_map.find(addr);
|
||||
if (prev != addr_tag_map.end()) {
|
||||
assert(prev->second != tag);
|
||||
++prev_alloc_tdc_cnt;
|
||||
addr_tag_map.erase(addr);
|
||||
}
|
||||
|
||||
addr_tag_map.emplace(addr, tag);
|
||||
|
||||
for (size_t j = 0; j < alloc_size; ++j) {
|
||||
// check that slot is zeroed
|
||||
assert(p[j] == 0);
|
||||
// check that slot is readable and writable
|
||||
p[j]++;
|
||||
}
|
||||
|
||||
allocations[i] = addr;
|
||||
}
|
||||
|
||||
// free some of allocations to allow their slots to be reused
|
||||
for (int i = sc.slot_cnt - 1; i >= 0; i -= 2) {
|
||||
free((void *) allocations[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// check that all of the tags were used, except reserved ones
|
||||
assert(seen_tags == (0xffff & ~(1 << 0 | 1 << 15)));
|
||||
|
||||
printf("size_class\t%i\t" "tdc_left %i\t" "tdc_right %i\t" "tdc_prev_alloc %i\n",
|
||||
sc.size, left_neighbor_tdc_cnt, right_neighbor_tdc_cnt, prev_alloc_tdc_cnt);
|
||||
|
||||
// make sure tag distinctess checks were actually performed
|
||||
int min_tdc_cnt = sc.slot_cnt * iter_cnt / 5;
|
||||
|
||||
assert(prev_alloc_tdc_cnt > min_tdc_cnt);
|
||||
|
||||
if (sc.slot_cnt > 1) {
|
||||
assert(left_neighbor_tdc_cnt > min_tdc_cnt);
|
||||
assert(right_neighbor_tdc_cnt > min_tdc_cnt);
|
||||
}
|
||||
|
||||
// async tag check failures are reported on context switch
|
||||
do_context_switch();
|
||||
}
|
||||
|
||||
printf("\nTag use counters:\n");
|
||||
|
||||
int min = INT_MAX;
|
||||
int max = 0;
|
||||
double geomean = 0.0;
|
||||
for (int i = min_tag; i <= max_tag; ++i) {
|
||||
int v = tag_usage[i];
|
||||
geomean += log(v);
|
||||
min = std::min(min, v);
|
||||
max = std::max(max, v);
|
||||
printf("%i\t%i\n", i, tag_usage[i]);
|
||||
}
|
||||
int tag_cnt = 1 + max_tag - min_tag;
|
||||
geomean = exp(geomean / tag_cnt);
|
||||
|
||||
double max_deviation = std::max((double) max - geomean, geomean - min);
|
||||
|
||||
printf("geomean: %.2f, max deviation from geomean: %.2f%%\n", geomean, (100.0 * max_deviation) / geomean);
|
||||
}
|
||||
|
||||
u8* alloc_default() {
|
||||
const size_t full_alloc_size = DEFAULT_ALLOC_SIZE + CANARY_SIZE;
|
||||
set<uptr> addrs;
|
||||
|
||||
// make sure allocation has both left and right neighbors, otherwise overflow/underflow tests
|
||||
// will fail when allocation is at the end/beginning of slab
|
||||
for (;;) {
|
||||
u8 *p = (u8 *) malloc(DEFAULT_ALLOC_SIZE);
|
||||
assert(p);
|
||||
|
||||
uptr addr = (uptr) untag_pointer(p);
|
||||
uptr left = addr - full_alloc_size;
|
||||
if (addrs.find(left) != addrs.end()) {
|
||||
uptr right = addr + full_alloc_size;
|
||||
if (addrs.find(right) != addrs.end()) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
addrs.emplace(addr);
|
||||
}
|
||||
}
|
||||
|
||||
volatile u8 u8_var;
|
||||
|
||||
void read_after_free() {
|
||||
u8 *p = alloc_default();
|
||||
free(p);
|
||||
volatile u8 v = p[0];
|
||||
(void) v;
|
||||
}
|
||||
|
||||
void write_after_free() {
|
||||
u8 *p = alloc_default();
|
||||
free(p);
|
||||
p[0] = 1;
|
||||
}
|
||||
|
||||
void underflow_read() {
|
||||
u8 *p = alloc_default();
|
||||
volatile u8 v = p[-1];
|
||||
(void) v;
|
||||
}
|
||||
|
||||
void underflow_write() {
|
||||
u8 *p = alloc_default();
|
||||
p[-1] = 1;
|
||||
}
|
||||
|
||||
void overflow_read() {
|
||||
u8 *p = alloc_default();
|
||||
volatile u8 v = p[DEFAULT_ALLOC_SIZE + CANARY_SIZE];
|
||||
(void) v;
|
||||
}
|
||||
|
||||
void overflow_write() {
|
||||
u8 *p = alloc_default();
|
||||
p[DEFAULT_ALLOC_SIZE + CANARY_SIZE] = 1;
|
||||
}
|
||||
|
||||
void untagged_read() {
|
||||
u8 *p = alloc_default();
|
||||
p = (u8 *) untag_pointer(p);
|
||||
volatile u8 v = p[0];
|
||||
(void) v;
|
||||
}
|
||||
|
||||
void untagged_write() {
|
||||
u8 *p = alloc_default();
|
||||
p = (u8 *) untag_pointer(p);
|
||||
p[0] = 1;
|
||||
}
|
||||
|
||||
map<string, function<void()>> tests = {
|
||||
#define TEST(s) { #s, s }
|
||||
TEST(tag_distinctness),
|
||||
TEST(read_after_free),
|
||||
TEST(write_after_free),
|
||||
TEST(overflow_read),
|
||||
TEST(overflow_write),
|
||||
TEST(underflow_read),
|
||||
TEST(underflow_write),
|
||||
TEST(untagged_read),
|
||||
TEST(untagged_write),
|
||||
#undef TEST
|
||||
};
|
||||
|
||||
void segv_handler(int, siginfo_t *si, void *) {
|
||||
fprintf(stderr, "SEGV_CODE %i", si->si_code);
|
||||
exit(139); // standard exit code for SIGSEGV
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
setbuf(stdout, NULL);
|
||||
assert(argc == 2);
|
||||
|
||||
auto test_name = string(argv[1]);
|
||||
auto test_fn = tests[test_name];
|
||||
assert(test_fn != nullptr);
|
||||
|
||||
assert(mallopt(M_BIONIC_SET_HEAP_TAGGING_LEVEL, M_HEAP_TAGGING_LEVEL_ASYNC) == 1);
|
||||
|
||||
struct sigaction sa = {
|
||||
.sa_sigaction = segv_handler,
|
||||
.sa_flags = SA_SIGINFO,
|
||||
};
|
||||
|
||||
assert(sigaction(SIGSEGV, &sa, nullptr) == 0);
|
||||
|
||||
test_fn();
|
||||
do_context_switch();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
package grapheneos.hmalloc;
|
||||
|
||||
import com.android.tradefed.device.DeviceNotAvailableException;
|
||||
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
|
||||
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
@RunWith(DeviceJUnit4ClassRunner.class)
|
||||
public class MemtagTest extends BaseHostJUnit4Test {
|
||||
|
||||
private static final String TEST_BINARY = "/data/local/tmp/memtag_test";
|
||||
|
||||
enum Result {
|
||||
SUCCESS(0, ""),
|
||||
// it's expected that the device is configured to use asymm MTE tag checking mode
|
||||
ASYNC_MTE_ERROR(139, "SEGV_CODE 8"),
|
||||
SYNC_MTE_ERROR(139, "SEGV_CODE 9"),
|
||||
;
|
||||
|
||||
public final int exitCode;
|
||||
public final String stderr;
|
||||
|
||||
Result(int exitCode, String stderr) {
|
||||
this.exitCode = exitCode;
|
||||
this.stderr = stderr;
|
||||
}
|
||||
}
|
||||
|
||||
private static final int SEGV_EXIT_CODE = 139;
|
||||
|
||||
private void runTest(String name, Result expectedResult) throws DeviceNotAvailableException {
|
||||
var args = new ArrayList<String>();
|
||||
args.add(TEST_BINARY);
|
||||
args.add(name);
|
||||
String cmdLine = String.join(" ", args);
|
||||
|
||||
var result = getDevice().executeShellV2Command(cmdLine);
|
||||
|
||||
assertEquals("process exit code", expectedResult.exitCode, result.getExitCode().intValue());
|
||||
assertEquals("stderr", expectedResult.stderr, result.getStderr());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tag_distinctness() throws DeviceNotAvailableException {
|
||||
runTest("tag_distinctness", Result.SUCCESS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void read_after_free() throws DeviceNotAvailableException {
|
||||
runTest("read_after_free", Result.SYNC_MTE_ERROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void write_after_free() throws DeviceNotAvailableException {
|
||||
runTest("write_after_free", Result.ASYNC_MTE_ERROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void underflow_read() throws DeviceNotAvailableException {
|
||||
runTest("underflow_read", Result.SYNC_MTE_ERROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void underflow_write() throws DeviceNotAvailableException {
|
||||
runTest("underflow_write", Result.ASYNC_MTE_ERROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void overflow_read() throws DeviceNotAvailableException {
|
||||
runTest("overflow_read", Result.SYNC_MTE_ERROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void overflow_write() throws DeviceNotAvailableException {
|
||||
runTest("overflow_write", Result.ASYNC_MTE_ERROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void untagged_read() throws DeviceNotAvailableException {
|
||||
runTest("untagged_read", Result.SYNC_MTE_ERROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void untagged_write() throws DeviceNotAvailableException {
|
||||
runTest("untagged_write", Result.ASYNC_MTE_ERROR);
|
||||
}
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
#ifndef ARM_MTE_H
|
||||
#define ARM_MTE_H
|
||||
|
||||
#include <arm_acle.h>
|
||||
#include <util.h>
|
||||
|
||||
// Returns a tagged pointer.
|
||||
// See https://developer.arm.com/documentation/ddi0602/2023-09/Base-Instructions/IRG--Insert-Random-Tag-
|
||||
static inline void *arm_mte_create_random_tag(void *p, u64 exclusion_mask) {
|
||||
return __arm_mte_create_random_tag(p, exclusion_mask);
|
||||
}
|
||||
|
||||
// Tag the memory region with the tag specified in tag bits of tagged_ptr. Memory region itself is
|
||||
// zeroed.
|
||||
// tagged_ptr has to be aligned by 16, and len has to be a multiple of 16 (tag granule size).
|
||||
//
|
||||
// Arm's software optimization guide says:
|
||||
// "it is recommended to use STZGM (or DCZGVA) to set tag if data is not a concern." (STZGM and
|
||||
// DCGZVA are zeroing variants of tagging instructions).
|
||||
//
|
||||
// Contents of this function were copied from scudo:
|
||||
// https://android.googlesource.com/platform/external/scudo/+/refs/tags/android-14.0.0_r1/standalone/memtag.h#167
|
||||
//
|
||||
// scudo is licensed under the Apache License v2.0 with LLVM Exceptions, which is compatible with
|
||||
// the hardened_malloc's MIT license
|
||||
static inline void arm_mte_tag_and_clear_mem(void *tagged_ptr, size_t len) {
|
||||
uintptr_t Begin = (uintptr_t) tagged_ptr;
|
||||
uintptr_t End = Begin + len;
|
||||
uintptr_t LineSize, Next, Tmp;
|
||||
__asm__ __volatile__(
|
||||
".arch_extension memtag \n\t"
|
||||
|
||||
// Compute the cache line size in bytes (DCZID_EL0 stores it as the log2
|
||||
// of the number of 4-byte words) and bail out to the slow path if DCZID_EL0
|
||||
// indicates that the DC instructions are unavailable.
|
||||
"DCZID .req %[Tmp] \n\t"
|
||||
"mrs DCZID, dczid_el0 \n\t"
|
||||
"tbnz DCZID, #4, 3f \n\t"
|
||||
"and DCZID, DCZID, #15 \n\t"
|
||||
"mov %[LineSize], #4 \n\t"
|
||||
"lsl %[LineSize], %[LineSize], DCZID \n\t"
|
||||
".unreq DCZID \n\t"
|
||||
|
||||
// Our main loop doesn't handle the case where we don't need to perform any
|
||||
// DC GZVA operations. If the size of our tagged region is less than
|
||||
// twice the cache line size, bail out to the slow path since it's not
|
||||
// guaranteed that we'll be able to do a DC GZVA.
|
||||
"Size .req %[Tmp] \n\t"
|
||||
"sub Size, %[End], %[Cur] \n\t"
|
||||
"cmp Size, %[LineSize], lsl #1 \n\t"
|
||||
"b.lt 3f \n\t"
|
||||
".unreq Size \n\t"
|
||||
|
||||
"LineMask .req %[Tmp] \n\t"
|
||||
"sub LineMask, %[LineSize], #1 \n\t"
|
||||
|
||||
// STZG until the start of the next cache line.
|
||||
"orr %[Next], %[Cur], LineMask \n\t"
|
||||
|
||||
"1:\n\t"
|
||||
"stzg %[Cur], [%[Cur]], #16 \n\t"
|
||||
"cmp %[Cur], %[Next] \n\t"
|
||||
"b.lt 1b \n\t"
|
||||
|
||||
// DC GZVA cache lines until we have no more full cache lines.
|
||||
"bic %[Next], %[End], LineMask \n\t"
|
||||
".unreq LineMask \n\t"
|
||||
|
||||
"2: \n\t"
|
||||
"dc gzva, %[Cur] \n\t"
|
||||
"add %[Cur], %[Cur], %[LineSize] \n\t"
|
||||
"cmp %[Cur], %[Next] \n\t"
|
||||
"b.lt 2b \n\t"
|
||||
|
||||
// STZG until the end of the tagged region. This loop is also used to handle
|
||||
// slow path cases.
|
||||
|
||||
"3: \n\t"
|
||||
"cmp %[Cur], %[End] \n\t"
|
||||
"b.ge 4f \n\t"
|
||||
"stzg %[Cur], [%[Cur]], #16 \n\t"
|
||||
"b 3b \n\t"
|
||||
|
||||
"4: \n\t"
|
||||
|
||||
: [Cur] "+&r"(Begin), [LineSize] "=&r"(LineSize), [Next] "=&r"(Next), [Tmp] "=&r"(Tmp)
|
||||
: [End] "r"(End)
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
#endif
|
|
@ -1,81 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from sys import argv
|
||||
|
||||
size_classes = [
|
||||
16, 32, 48, 64, 80, 96, 112, 128,
|
||||
160, 192, 224, 256,
|
||||
320, 384, 448, 512,
|
||||
640, 768, 896, 1024,
|
||||
1280, 1536, 1792, 2048,
|
||||
2560, 3072, 3584, 4096,
|
||||
5120, 6144, 7168, 8192,
|
||||
10240, 12288, 14336, 16384,
|
||||
20480, 24576, 28672, 32768,
|
||||
40960, 49152, 57344, 65536,
|
||||
81920, 98304, 114688, 131072,
|
||||
]
|
||||
|
||||
size_class_slots = [
|
||||
256, 128, 85, 64, 51, 42, 36, 64,
|
||||
51, 64, 54, 64,
|
||||
64, 64, 64, 64,
|
||||
64, 64, 64, 64,
|
||||
16, 16, 16, 16,
|
||||
8, 8, 8, 8,
|
||||
8, 8, 8, 8,
|
||||
6, 5, 4, 4,
|
||||
2, 2, 2, 2,
|
||||
1, 1, 1, 1,
|
||||
1, 1, 1, 1,
|
||||
]
|
||||
|
||||
fragmentation = [100 - 1 / 16 * 100]
|
||||
|
||||
for i in range(len(size_classes) - 1):
|
||||
size_class = size_classes[i + 1]
|
||||
worst_case = size_classes[i] + 1
|
||||
used = worst_case / size_class
|
||||
fragmentation.append(100 - used * 100);
|
||||
|
||||
def page_align(size):
|
||||
return (size + 4095) & ~4095
|
||||
|
||||
print("| ", end="")
|
||||
print("size class", "worst case internal fragmentation", "slab slots", "slab size", "internal fragmentation for slabs", sep=" | ", end=" |\n")
|
||||
print("| ", end='')
|
||||
print("-", "-", "-", "-", "-", sep=" | ", end=" |\n")
|
||||
for size, slots, fragmentation in zip(size_classes, size_class_slots, fragmentation):
|
||||
used = size * slots
|
||||
real = page_align(used)
|
||||
print("| ", end='')
|
||||
print(size, f"{fragmentation:.4}%", slots, real, str(100 - used / real * 100) + "%", sep=" | ", end=" |\n")
|
||||
|
||||
if len(argv) < 2:
|
||||
exit()
|
||||
|
||||
max_bits = 256
|
||||
max_page_span = 16
|
||||
|
||||
print()
|
||||
|
||||
print("maximum bitmap size is {}-bit".format(max_bits))
|
||||
print("maximum page span size is {} ({})".format(max_page_span, max_page_span * 4096))
|
||||
|
||||
for size_class in size_classes:
|
||||
choices = []
|
||||
for bits in range(1, max_bits + 1):
|
||||
used = size_class * bits
|
||||
real = page_align(used)
|
||||
if real > 65536:
|
||||
continue
|
||||
pages = real / 4096
|
||||
efficiency = used / real * 100
|
||||
choices.append((bits, used, real, pages, efficiency))
|
||||
|
||||
choices.sort(key=lambda x: x[4], reverse=True)
|
||||
|
||||
print()
|
||||
print("size_class:", size_class)
|
||||
for choice in choices[:10]:
|
||||
print(choice)
|
|
@ -1,177 +0,0 @@
|
|||
// Based on chacha-merged.c version 20080118
|
||||
// D. J. Bernstein
|
||||
// Public domain.
|
||||
|
||||
#include "chacha.h"
|
||||
|
||||
// ChaCha8
|
||||
static const unsigned rounds = 8;
|
||||
|
||||
#define U8C(v) (v##U)
|
||||
#define U32C(v) (v##U)
|
||||
|
||||
#define U8V(v) ((u8)(v) & U8C(0xFF))
|
||||
#define U32V(v) ((u32)(v) & U32C(0xFFFFFFFF))
|
||||
|
||||
#define ROTL32(v, n) \
|
||||
(U32V((v) << (n)) | ((v) >> (32 - (n))))
|
||||
|
||||
#define U8TO32_LITTLE(p) \
|
||||
(((u32)((p)[0])) | \
|
||||
((u32)((p)[1]) << 8) | \
|
||||
((u32)((p)[2]) << 16) | \
|
||||
((u32)((p)[3]) << 24))
|
||||
|
||||
#define U32TO8_LITTLE(p, v) \
|
||||
do { \
|
||||
(p)[0] = U8V((v)); \
|
||||
(p)[1] = U8V((v) >> 8); \
|
||||
(p)[2] = U8V((v) >> 16); \
|
||||
(p)[3] = U8V((v) >> 24); \
|
||||
} while (0)
|
||||
|
||||
#define ROTATE(v, c) (ROTL32(v, c))
|
||||
#define XOR(v, w) ((v) ^ (w))
|
||||
#define PLUS(v, w) (U32V((v) + (w)))
|
||||
#define PLUSONE(v) (PLUS((v), 1))
|
||||
|
||||
#define QUARTERROUND(a, b, c, d) \
|
||||
a = PLUS(a, b); d = ROTATE(XOR(d, a), 16); \
|
||||
c = PLUS(c, d); b = ROTATE(XOR(b, c), 12); \
|
||||
a = PLUS(a, b); d = ROTATE(XOR(d, a), 8); \
|
||||
c = PLUS(c, d); b = ROTATE(XOR(b, c), 7);
|
||||
|
||||
static const char sigma[16] = "expand 32-byte k";
|
||||
|
||||
void chacha_keysetup(chacha_ctx *x, const u8 *k) {
|
||||
x->input[0] = U8TO32_LITTLE(sigma + 0);
|
||||
x->input[1] = U8TO32_LITTLE(sigma + 4);
|
||||
x->input[2] = U8TO32_LITTLE(sigma + 8);
|
||||
x->input[3] = U8TO32_LITTLE(sigma + 12);
|
||||
x->input[4] = U8TO32_LITTLE(k + 0);
|
||||
x->input[5] = U8TO32_LITTLE(k + 4);
|
||||
x->input[6] = U8TO32_LITTLE(k + 8);
|
||||
x->input[7] = U8TO32_LITTLE(k + 12);
|
||||
x->input[8] = U8TO32_LITTLE(k + 16);
|
||||
x->input[9] = U8TO32_LITTLE(k + 20);
|
||||
x->input[10] = U8TO32_LITTLE(k + 24);
|
||||
x->input[11] = U8TO32_LITTLE(k + 28);
|
||||
}
|
||||
|
||||
void chacha_ivsetup(chacha_ctx *x, const u8 *iv) {
|
||||
x->input[12] = 0;
|
||||
x->input[13] = 0;
|
||||
x->input[14] = U8TO32_LITTLE(iv + 0);
|
||||
x->input[15] = U8TO32_LITTLE(iv + 4);
|
||||
}
|
||||
|
||||
void chacha_keystream_bytes(chacha_ctx *x, u8 *c, u32 bytes) {
|
||||
if (!bytes) {
|
||||
return;
|
||||
}
|
||||
|
||||
u8 *ctarget;
|
||||
u8 tmp[64];
|
||||
|
||||
u32 j0 = x->input[0];
|
||||
u32 j1 = x->input[1];
|
||||
u32 j2 = x->input[2];
|
||||
u32 j3 = x->input[3];
|
||||
u32 j4 = x->input[4];
|
||||
u32 j5 = x->input[5];
|
||||
u32 j6 = x->input[6];
|
||||
u32 j7 = x->input[7];
|
||||
u32 j8 = x->input[8];
|
||||
u32 j9 = x->input[9];
|
||||
u32 j10 = x->input[10];
|
||||
u32 j11 = x->input[11];
|
||||
u32 j12 = x->input[12];
|
||||
u32 j13 = x->input[13];
|
||||
u32 j14 = x->input[14];
|
||||
u32 j15 = x->input[15];
|
||||
|
||||
for (;;) {
|
||||
if (bytes < 64) {
|
||||
ctarget = c;
|
||||
c = tmp;
|
||||
}
|
||||
u32 x0 = j0;
|
||||
u32 x1 = j1;
|
||||
u32 x2 = j2;
|
||||
u32 x3 = j3;
|
||||
u32 x4 = j4;
|
||||
u32 x5 = j5;
|
||||
u32 x6 = j6;
|
||||
u32 x7 = j7;
|
||||
u32 x8 = j8;
|
||||
u32 x9 = j9;
|
||||
u32 x10 = j10;
|
||||
u32 x11 = j11;
|
||||
u32 x12 = j12;
|
||||
u32 x13 = j13;
|
||||
u32 x14 = j14;
|
||||
u32 x15 = j15;
|
||||
for (unsigned i = rounds; i > 0; i -= 2) {
|
||||
QUARTERROUND(x0, x4, x8, x12)
|
||||
QUARTERROUND(x1, x5, x9, x13)
|
||||
QUARTERROUND(x2, x6, x10, x14)
|
||||
QUARTERROUND(x3, x7, x11, x15)
|
||||
QUARTERROUND(x0, x5, x10, x15)
|
||||
QUARTERROUND(x1, x6, x11, x12)
|
||||
QUARTERROUND(x2, x7, x8, x13)
|
||||
QUARTERROUND(x3, x4, x9, x14)
|
||||
}
|
||||
x0 = PLUS(x0, j0);
|
||||
x1 = PLUS(x1, j1);
|
||||
x2 = PLUS(x2, j2);
|
||||
x3 = PLUS(x3, j3);
|
||||
x4 = PLUS(x4, j4);
|
||||
x5 = PLUS(x5, j5);
|
||||
x6 = PLUS(x6, j6);
|
||||
x7 = PLUS(x7, j7);
|
||||
x8 = PLUS(x8, j8);
|
||||
x9 = PLUS(x9, j9);
|
||||
x10 = PLUS(x10, j10);
|
||||
x11 = PLUS(x11, j11);
|
||||
x12 = PLUS(x12, j12);
|
||||
x13 = PLUS(x13, j13);
|
||||
x14 = PLUS(x14, j14);
|
||||
x15 = PLUS(x15, j15);
|
||||
|
||||
j12 = PLUSONE(j12);
|
||||
if (!j12) {
|
||||
j13 = PLUSONE(j13);
|
||||
// stopping at 2^70 bytes per nonce is user's responsibility
|
||||
}
|
||||
|
||||
U32TO8_LITTLE(c + 0, x0);
|
||||
U32TO8_LITTLE(c + 4, x1);
|
||||
U32TO8_LITTLE(c + 8, x2);
|
||||
U32TO8_LITTLE(c + 12, x3);
|
||||
U32TO8_LITTLE(c + 16, x4);
|
||||
U32TO8_LITTLE(c + 20, x5);
|
||||
U32TO8_LITTLE(c + 24, x6);
|
||||
U32TO8_LITTLE(c + 28, x7);
|
||||
U32TO8_LITTLE(c + 32, x8);
|
||||
U32TO8_LITTLE(c + 36, x9);
|
||||
U32TO8_LITTLE(c + 40, x10);
|
||||
U32TO8_LITTLE(c + 44, x11);
|
||||
U32TO8_LITTLE(c + 48, x12);
|
||||
U32TO8_LITTLE(c + 52, x13);
|
||||
U32TO8_LITTLE(c + 56, x14);
|
||||
U32TO8_LITTLE(c + 60, x15);
|
||||
|
||||
if (bytes <= 64) {
|
||||
if (bytes < 64) {
|
||||
for (unsigned i = 0; i < bytes; ++i) {
|
||||
ctarget[i] = c[i];
|
||||
}
|
||||
}
|
||||
x->input[12] = j12;
|
||||
x->input[13] = j13;
|
||||
return;
|
||||
}
|
||||
bytes -= 64;
|
||||
c += 64;
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
#ifndef CHACHA_H
|
||||
#define CHACHA_H
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#define CHACHA_KEY_SIZE 32
|
||||
#define CHACHA_IV_SIZE 8
|
||||
|
||||
typedef struct {
|
||||
u32 input[16];
|
||||
} chacha_ctx;
|
||||
|
||||
void chacha_keysetup(chacha_ctx *x, const u8 *k);
|
||||
void chacha_ivsetup(chacha_ctx *x, const u8 *iv);
|
||||
void chacha_keystream_bytes(chacha_ctx *x, u8 *c, u32 bytes);
|
||||
|
||||
#endif
|
|
@ -1,23 +0,0 @@
|
|||
CONFIG_WERROR := true
|
||||
CONFIG_NATIVE := true
|
||||
CONFIG_CXX_ALLOCATOR := true
|
||||
CONFIG_UBSAN := false
|
||||
CONFIG_SEAL_METADATA := false
|
||||
CONFIG_ZERO_ON_FREE := true
|
||||
CONFIG_WRITE_AFTER_FREE_CHECK := true
|
||||
CONFIG_SLOT_RANDOMIZE := true
|
||||
CONFIG_SLAB_CANARY := true
|
||||
CONFIG_SLAB_QUARANTINE_RANDOM_LENGTH := 1
|
||||
CONFIG_SLAB_QUARANTINE_QUEUE_LENGTH := 1
|
||||
CONFIG_EXTENDED_SIZE_CLASSES := true
|
||||
CONFIG_LARGE_SIZE_CLASSES := true
|
||||
CONFIG_GUARD_SLABS_INTERVAL := 1
|
||||
CONFIG_GUARD_SIZE_DIVISOR := 2
|
||||
CONFIG_REGION_QUARANTINE_RANDOM_LENGTH := 256
|
||||
CONFIG_REGION_QUARANTINE_QUEUE_LENGTH := 1024
|
||||
CONFIG_REGION_QUARANTINE_SKIP_THRESHOLD := 33554432 # 32MiB
|
||||
CONFIG_FREE_SLABS_QUARANTINE_RANDOM_LENGTH := 32
|
||||
CONFIG_CLASS_REGION_SIZE := 34359738368 # 32GiB
|
||||
CONFIG_N_ARENA := 4
|
||||
CONFIG_STATS := false
|
||||
CONFIG_SELF_INIT := true
|
|
@ -1,23 +0,0 @@
|
|||
CONFIG_WERROR := true
|
||||
CONFIG_NATIVE := true
|
||||
CONFIG_CXX_ALLOCATOR := true
|
||||
CONFIG_UBSAN := false
|
||||
CONFIG_SEAL_METADATA := false
|
||||
CONFIG_ZERO_ON_FREE := true
|
||||
CONFIG_WRITE_AFTER_FREE_CHECK := false
|
||||
CONFIG_SLOT_RANDOMIZE := false
|
||||
CONFIG_SLAB_CANARY := true
|
||||
CONFIG_SLAB_QUARANTINE_RANDOM_LENGTH := 0
|
||||
CONFIG_SLAB_QUARANTINE_QUEUE_LENGTH := 0
|
||||
CONFIG_EXTENDED_SIZE_CLASSES := true
|
||||
CONFIG_LARGE_SIZE_CLASSES := true
|
||||
CONFIG_GUARD_SLABS_INTERVAL := 8
|
||||
CONFIG_GUARD_SIZE_DIVISOR := 2
|
||||
CONFIG_REGION_QUARANTINE_RANDOM_LENGTH := 256
|
||||
CONFIG_REGION_QUARANTINE_QUEUE_LENGTH := 1024
|
||||
CONFIG_REGION_QUARANTINE_SKIP_THRESHOLD := 33554432 # 32MiB
|
||||
CONFIG_FREE_SLABS_QUARANTINE_RANDOM_LENGTH := 32
|
||||
CONFIG_CLASS_REGION_SIZE := 34359738368 # 32GiB
|
||||
CONFIG_N_ARENA := 4
|
||||
CONFIG_STATS := false
|
||||
CONFIG_SELF_INIT := true
|
File diff suppressed because it is too large
Load diff
|
@ -1,129 +0,0 @@
|
|||
#ifndef ALLOCATOR_H
|
||||
#define ALLOCATOR_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <malloc.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef H_MALLOC_PREFIX
|
||||
#define h_malloc malloc
|
||||
#define h_calloc calloc
|
||||
#define h_realloc realloc
|
||||
#define h_aligned_alloc aligned_alloc
|
||||
#define h_free free
|
||||
|
||||
#define h_posix_memalign posix_memalign
|
||||
|
||||
#define h_malloc_usable_size malloc_usable_size
|
||||
#define h_mallopt mallopt
|
||||
#define h_malloc_trim malloc_trim
|
||||
#define h_malloc_stats malloc_stats
|
||||
#define h_mallinfo mallinfo
|
||||
#define h_mallinfo2 mallinfo2
|
||||
#define h_malloc_info malloc_info
|
||||
|
||||
#define h_memalign memalign
|
||||
#define h_valloc valloc
|
||||
#define h_pvalloc pvalloc
|
||||
#define h_cfree cfree
|
||||
#define h_malloc_get_state malloc_get_state
|
||||
#define h_malloc_set_state malloc_set_state
|
||||
|
||||
#define h_mallinfo_narenas mallinfo_narenas
|
||||
#define h_mallinfo_nbins mallinfo_nbins
|
||||
#define h_mallinfo_arena_info mallinfo_arena_info
|
||||
#define h_mallinfo_bin_info mallinfo_bin_info
|
||||
|
||||
#define h_malloc_iterate malloc_iterate
|
||||
#define h_malloc_disable malloc_disable
|
||||
#define h_malloc_enable malloc_enable
|
||||
|
||||
#define h_malloc_object_size malloc_object_size
|
||||
#define h_malloc_object_size_fast malloc_object_size_fast
|
||||
#define h_free_sized free_sized
|
||||
#endif
|
||||
|
||||
// C standard
|
||||
__attribute__((malloc)) __attribute__((alloc_size(1))) void *h_malloc(size_t size);
|
||||
__attribute__((malloc)) __attribute__((alloc_size(1, 2))) void *h_calloc(size_t nmemb, size_t size);
|
||||
__attribute__((alloc_size(2))) void *h_realloc(void *ptr, size_t size);
|
||||
__attribute__((malloc)) __attribute__((alloc_size(2))) __attribute__((alloc_align(1)))
|
||||
void *h_aligned_alloc(size_t alignment, size_t size);
|
||||
void h_free(void *ptr);
|
||||
|
||||
// POSIX
|
||||
int h_posix_memalign(void **memptr, size_t alignment, size_t size);
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#define H_MALLOC_USABLE_SIZE_CONST const
|
||||
#else
|
||||
#define H_MALLOC_USABLE_SIZE_CONST
|
||||
#endif
|
||||
|
||||
// glibc extensions
|
||||
size_t h_malloc_usable_size(H_MALLOC_USABLE_SIZE_CONST void *ptr);
|
||||
int h_mallopt(int param, int value);
|
||||
int h_malloc_trim(size_t pad);
|
||||
void h_malloc_stats(void);
|
||||
#if defined(__GLIBC__) || defined(__ANDROID__)
|
||||
struct mallinfo h_mallinfo(void);
|
||||
#endif
|
||||
#ifndef __ANDROID__
|
||||
int h_malloc_info(int options, FILE *fp);
|
||||
#endif
|
||||
|
||||
// obsolete glibc extensions
|
||||
__attribute__((malloc)) __attribute__((alloc_size(2))) __attribute__((alloc_align(1)))
|
||||
void *h_memalign(size_t alignment, size_t size);
|
||||
#ifndef __ANDROID__
|
||||
__attribute__((malloc)) __attribute__((alloc_size(1))) void *h_valloc(size_t size);
|
||||
__attribute__((malloc)) void *h_pvalloc(size_t size);
|
||||
#endif
|
||||
#ifdef __GLIBC__
|
||||
void h_cfree(void *ptr) __THROW;
|
||||
void *h_malloc_get_state(void);
|
||||
int h_malloc_set_state(void *state);
|
||||
#endif
|
||||
|
||||
// Android extensions
|
||||
#ifdef __ANDROID__
|
||||
size_t h_mallinfo_narenas(void);
|
||||
size_t h_mallinfo_nbins(void);
|
||||
struct mallinfo h_mallinfo_arena_info(size_t arena);
|
||||
struct mallinfo h_mallinfo_bin_info(size_t arena, size_t bin);
|
||||
int h_malloc_iterate(uintptr_t base, size_t size, void (*callback)(uintptr_t ptr, size_t size, void *arg),
|
||||
void *arg);
|
||||
void h_malloc_disable(void);
|
||||
void h_malloc_enable(void);
|
||||
void h_malloc_disable_memory_tagging(void);
|
||||
#endif
|
||||
|
||||
// hardened_malloc extensions
|
||||
|
||||
// return an upper bound on object size for any pointer based on malloc metadata
|
||||
size_t h_malloc_object_size(const void *ptr);
|
||||
|
||||
// similar to malloc_object_size, but avoiding locking so the results are much more limited
|
||||
size_t h_malloc_object_size_fast(const void *ptr);
|
||||
|
||||
// The free function with an extra parameter for passing the size requested at
|
||||
// allocation time.
|
||||
//
|
||||
// This offers the same functionality as C++14 sized deallocation and can be
|
||||
// used to implement it.
|
||||
//
|
||||
// A performance-oriented allocator would use this as a performance
|
||||
// enhancement with undefined behavior on a mismatch. Instead, this hardened
|
||||
// allocator implementation uses it to improve security by checking that the
|
||||
// passed size matches the allocated size.
|
||||
void h_free_sized(void *ptr, size_t expected_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -1,120 +0,0 @@
|
|||
#include <errno.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#ifdef LABEL_MEMORY
|
||||
#include <sys/prctl.h>
|
||||
#endif
|
||||
|
||||
#ifndef PR_SET_VMA
|
||||
#define PR_SET_VMA 0x53564d41
|
||||
#endif
|
||||
|
||||
#ifndef PR_SET_VMA_ANON_NAME
|
||||
#define PR_SET_VMA_ANON_NAME 0
|
||||
#endif
|
||||
|
||||
#include "memory.h"
|
||||
#include "util.h"
|
||||
|
||||
void *memory_map(size_t size) {
|
||||
void *p = mmap(NULL, size, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
|
||||
if (unlikely(p == MAP_FAILED)) {
|
||||
if (errno != ENOMEM) {
|
||||
fatal_error("non-ENOMEM mmap failure");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
#ifdef HAS_ARM_MTE
|
||||
// Note that PROT_MTE can't be cleared via mprotect
|
||||
void *memory_map_mte(size_t size) {
|
||||
void *p = mmap(NULL, size, PROT_MTE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
|
||||
if (unlikely(p == MAP_FAILED)) {
|
||||
if (errno != ENOMEM) {
|
||||
fatal_error("non-ENOMEM MTE mmap failure");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool memory_map_fixed(void *ptr, size_t size) {
|
||||
void *p = mmap(ptr, size, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED, -1, 0);
|
||||
bool ret = p == MAP_FAILED;
|
||||
if (unlikely(ret) && errno != ENOMEM) {
|
||||
fatal_error("non-ENOMEM MAP_FIXED mmap failure");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool memory_unmap(void *ptr, size_t size) {
|
||||
bool ret = munmap(ptr, size);
|
||||
if (unlikely(ret) && errno != ENOMEM) {
|
||||
fatal_error("non-ENOMEM munmap failure");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool memory_protect_prot(void *ptr, size_t size, int prot, UNUSED int pkey) {
|
||||
#ifdef USE_PKEY
|
||||
bool ret = pkey_mprotect(ptr, size, prot, pkey);
|
||||
#else
|
||||
bool ret = mprotect(ptr, size, prot);
|
||||
#endif
|
||||
if (unlikely(ret) && errno != ENOMEM) {
|
||||
fatal_error("non-ENOMEM mprotect failure");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool memory_protect_ro(void *ptr, size_t size) {
|
||||
return memory_protect_prot(ptr, size, PROT_READ, -1);
|
||||
}
|
||||
|
||||
bool memory_protect_rw(void *ptr, size_t size) {
|
||||
return memory_protect_prot(ptr, size, PROT_READ|PROT_WRITE, -1);
|
||||
}
|
||||
|
||||
bool memory_protect_rw_metadata(void *ptr, size_t size) {
|
||||
return memory_protect_prot(ptr, size, PROT_READ|PROT_WRITE, get_metadata_key());
|
||||
}
|
||||
|
||||
#ifdef HAVE_COMPATIBLE_MREMAP
|
||||
bool memory_remap(void *old, size_t old_size, size_t new_size) {
|
||||
void *ptr = mremap(old, old_size, new_size, 0);
|
||||
bool ret = ptr == MAP_FAILED;
|
||||
if (unlikely(ret) && errno != ENOMEM) {
|
||||
fatal_error("non-ENOMEM mremap failure");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool memory_remap_fixed(void *old, size_t old_size, void *new, size_t new_size) {
|
||||
void *ptr = mremap(old, old_size, new_size, MREMAP_MAYMOVE|MREMAP_FIXED, new);
|
||||
bool ret = ptr == MAP_FAILED;
|
||||
if (unlikely(ret) && errno != ENOMEM) {
|
||||
fatal_error("non-ENOMEM MREMAP_FIXED mremap failure");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool memory_purge(void *ptr, size_t size) {
|
||||
int ret = madvise(ptr, size, MADV_DONTNEED);
|
||||
if (unlikely(ret) && errno != ENOMEM) {
|
||||
fatal_error("non-ENOMEM MADV_DONTNEED madvise failure");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool memory_set_name(UNUSED void *ptr, UNUSED size_t size, UNUSED const char *name) {
|
||||
#ifdef LABEL_MEMORY
|
||||
return prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, ptr, size, name);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
#ifndef MEMORY_H
|
||||
#define MEMORY_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __linux__
|
||||
#define HAVE_COMPATIBLE_MREMAP
|
||||
#endif
|
||||
|
||||
int get_metadata_key(void);
|
||||
|
||||
void *memory_map(size_t size);
|
||||
#ifdef HAS_ARM_MTE
|
||||
void *memory_map_mte(size_t size);
|
||||
#endif
|
||||
bool memory_map_fixed(void *ptr, size_t size);
|
||||
bool memory_unmap(void *ptr, size_t size);
|
||||
bool memory_protect_ro(void *ptr, size_t size);
|
||||
bool memory_protect_rw(void *ptr, size_t size);
|
||||
bool memory_protect_rw_metadata(void *ptr, size_t size);
|
||||
#ifdef HAVE_COMPATIBLE_MREMAP
|
||||
bool memory_remap(void *old, size_t old_size, size_t new_size);
|
||||
bool memory_remap_fixed(void *old, size_t old_size, void *new, size_t new_size);
|
||||
#endif
|
||||
bool memory_purge(void *ptr, size_t size);
|
||||
bool memory_set_name(void *ptr, size_t size, const char *name);
|
||||
|
||||
#endif
|
|
@ -1,49 +0,0 @@
|
|||
#ifndef MEMTAG_H
|
||||
#define MEMTAG_H
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#ifdef HAS_ARM_MTE
|
||||
#include "arm_mte.h"
|
||||
#define MEMTAG 1
|
||||
#define RESERVED_TAG 15
|
||||
#define TAG_WIDTH 4
|
||||
#endif
|
||||
|
||||
static inline void *untag_pointer(void *ptr) {
|
||||
#ifdef HAS_ARM_MTE
|
||||
const uintptr_t mask = UINTPTR_MAX >> 8;
|
||||
return (void *) ((uintptr_t) ptr & mask);
|
||||
#else
|
||||
return ptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline const void *untag_const_pointer(const void *ptr) {
|
||||
#ifdef HAS_ARM_MTE
|
||||
const uintptr_t mask = UINTPTR_MAX >> 8;
|
||||
return (const void *) ((uintptr_t) ptr & mask);
|
||||
#else
|
||||
return ptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void *set_pointer_tag(void *ptr, u8 tag) {
|
||||
#ifdef HAS_ARM_MTE
|
||||
return (void *) (((uintptr_t) tag << 56) | (uintptr_t) untag_pointer(ptr));
|
||||
#else
|
||||
(void) tag;
|
||||
return ptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline u8 get_pointer_tag(void *ptr) {
|
||||
#ifdef HAS_ARM_MTE
|
||||
return (((uintptr_t) ptr) >> 56) & 0xf;
|
||||
#else
|
||||
(void) ptr;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,28 +0,0 @@
|
|||
#ifndef MUTEX_H
|
||||
#define MUTEX_H
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
struct mutex {
|
||||
pthread_mutex_t lock;
|
||||
};
|
||||
|
||||
#define MUTEX_INITIALIZER (struct mutex){PTHREAD_MUTEX_INITIALIZER}
|
||||
|
||||
static inline void mutex_init(struct mutex *m) {
|
||||
if (unlikely(pthread_mutex_init(&m->lock, NULL))) {
|
||||
fatal_error("mutex initialization failed");
|
||||
}
|
||||
}
|
||||
|
||||
static inline void mutex_lock(struct mutex *m) {
|
||||
pthread_mutex_lock(&m->lock);
|
||||
}
|
||||
|
||||
static inline void mutex_unlock(struct mutex *m) {
|
||||
pthread_mutex_unlock(&m->lock);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,153 +0,0 @@
|
|||
// needed with libstdc++ but not libc++
|
||||
#if __has_include(<bits/functexcept.h>)
|
||||
#include <bits/functexcept.h>
|
||||
#endif
|
||||
|
||||
#include <new>
|
||||
|
||||
#include "h_malloc.h"
|
||||
#include "util.h"
|
||||
|
||||
COLD static void *handle_out_of_memory(size_t size, bool nothrow) {
|
||||
void *ptr = nullptr;
|
||||
|
||||
do {
|
||||
std::new_handler handler = std::get_new_handler();
|
||||
if (handler == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
handler();
|
||||
} catch (const std::bad_alloc &) {
|
||||
break;
|
||||
}
|
||||
|
||||
ptr = h_malloc(size);
|
||||
} while (ptr == nullptr);
|
||||
|
||||
if (ptr == nullptr && !nothrow) {
|
||||
std::__throw_bad_alloc();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static inline void *new_impl(size_t size, bool nothrow) {
|
||||
void *ptr = h_malloc(size);
|
||||
if (likely(ptr != nullptr)) {
|
||||
return ptr;
|
||||
}
|
||||
return handle_out_of_memory(size, nothrow);
|
||||
}
|
||||
|
||||
EXPORT void *operator new(size_t size) {
|
||||
return new_impl(size, false);
|
||||
}
|
||||
|
||||
EXPORT void *operator new[](size_t size) {
|
||||
return new_impl(size, false);
|
||||
}
|
||||
|
||||
EXPORT void *operator new(size_t size, const std::nothrow_t &) noexcept {
|
||||
return new_impl(size, true);
|
||||
}
|
||||
|
||||
EXPORT void *operator new[](size_t size, const std::nothrow_t &) noexcept {
|
||||
return new_impl(size, true);
|
||||
}
|
||||
|
||||
EXPORT void operator delete(void *ptr) noexcept {
|
||||
h_free(ptr);
|
||||
}
|
||||
|
||||
EXPORT void operator delete[](void *ptr) noexcept {
|
||||
h_free(ptr);
|
||||
}
|
||||
|
||||
EXPORT void operator delete(void *ptr, const std::nothrow_t &) noexcept {
|
||||
h_free(ptr);
|
||||
}
|
||||
|
||||
EXPORT void operator delete[](void *ptr, const std::nothrow_t &) noexcept {
|
||||
h_free(ptr);
|
||||
}
|
||||
|
||||
EXPORT void operator delete(void *ptr, size_t size) noexcept {
|
||||
h_free_sized(ptr, size);
|
||||
}
|
||||
|
||||
EXPORT void operator delete[](void *ptr, size_t size) noexcept {
|
||||
h_free_sized(ptr, size);
|
||||
}
|
||||
|
||||
COLD static void *handle_out_of_memory(size_t size, size_t alignment, bool nothrow) {
|
||||
void *ptr = nullptr;
|
||||
|
||||
do {
|
||||
std::new_handler handler = std::get_new_handler();
|
||||
if (handler == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
handler();
|
||||
} catch (const std::bad_alloc &) {
|
||||
break;
|
||||
}
|
||||
|
||||
ptr = h_aligned_alloc(alignment, size);
|
||||
} while (ptr == nullptr);
|
||||
|
||||
if (ptr == nullptr && !nothrow) {
|
||||
std::__throw_bad_alloc();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static inline void *new_impl(size_t size, size_t alignment, bool nothrow) {
|
||||
void *ptr = h_aligned_alloc(alignment, size);
|
||||
if (likely(ptr != nullptr)) {
|
||||
return ptr;
|
||||
}
|
||||
return handle_out_of_memory(size, alignment, nothrow);
|
||||
}
|
||||
|
||||
EXPORT void *operator new(size_t size, std::align_val_t alignment) {
|
||||
return new_impl(size, static_cast<size_t>(alignment), false);
|
||||
}
|
||||
|
||||
EXPORT void *operator new[](size_t size, std::align_val_t alignment) {
|
||||
return new_impl(size, static_cast<size_t>(alignment), false);
|
||||
}
|
||||
|
||||
EXPORT void *operator new(size_t size, std::align_val_t alignment, const std::nothrow_t &) noexcept {
|
||||
return new_impl(size, static_cast<size_t>(alignment), true);
|
||||
}
|
||||
|
||||
EXPORT void *operator new[](size_t size, std::align_val_t alignment, const std::nothrow_t &) noexcept {
|
||||
return new_impl(size, static_cast<size_t>(alignment), true);
|
||||
}
|
||||
|
||||
EXPORT void operator delete(void *ptr, std::align_val_t) noexcept {
|
||||
h_free(ptr);
|
||||
}
|
||||
|
||||
EXPORT void operator delete[](void *ptr, std::align_val_t) noexcept {
|
||||
h_free(ptr);
|
||||
}
|
||||
|
||||
EXPORT void operator delete(void *ptr, std::align_val_t, const std::nothrow_t &) noexcept {
|
||||
h_free(ptr);
|
||||
}
|
||||
|
||||
EXPORT void operator delete[](void *ptr, std::align_val_t, const std::nothrow_t &) noexcept {
|
||||
h_free(ptr);
|
||||
}
|
||||
|
||||
EXPORT void operator delete(void *ptr, size_t size, std::align_val_t) noexcept {
|
||||
h_free_sized(ptr, size);
|
||||
}
|
||||
|
||||
EXPORT void operator delete[](void *ptr, size_t size, std::align_val_t) noexcept {
|
||||
h_free_sized(ptr, size);
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
#include <errno.h>
|
||||
|
||||
#include "memory.h"
|
||||
#include "pages.h"
|
||||
#include "util.h"
|
||||
|
||||
static bool add_guards(size_t size, size_t guard_size, size_t *total_size) {
|
||||
return __builtin_add_overflow(size, guard_size, total_size) ||
|
||||
__builtin_add_overflow(*total_size, guard_size, total_size);
|
||||
}
|
||||
|
||||
void *allocate_pages(size_t usable_size, size_t guard_size, bool unprotect, const char *name) {
|
||||
size_t real_size;
|
||||
if (unlikely(add_guards(usable_size, guard_size, &real_size))) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
void *real = memory_map(real_size);
|
||||
if (unlikely(real == NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
memory_set_name(real, real_size, name);
|
||||
void *usable = (char *)real + guard_size;
|
||||
if (unprotect && unlikely(memory_protect_rw(usable, usable_size))) {
|
||||
memory_unmap(real, real_size);
|
||||
return NULL;
|
||||
}
|
||||
return usable;
|
||||
}
|
||||
|
||||
void *allocate_pages_aligned(size_t usable_size, size_t alignment, size_t guard_size, const char *name) {
|
||||
usable_size = page_align(usable_size);
|
||||
if (unlikely(!usable_size)) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t alloc_size;
|
||||
if (unlikely(__builtin_add_overflow(usable_size, alignment - PAGE_SIZE, &alloc_size))) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t real_alloc_size;
|
||||
if (unlikely(add_guards(alloc_size, guard_size, &real_alloc_size))) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *real = memory_map(real_alloc_size);
|
||||
if (unlikely(real == NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
memory_set_name(real, real_alloc_size, name);
|
||||
|
||||
void *usable = (char *)real + guard_size;
|
||||
|
||||
size_t lead_size = align((uintptr_t)usable, alignment) - (uintptr_t)usable;
|
||||
size_t trail_size = alloc_size - lead_size - usable_size;
|
||||
void *base = (char *)usable + lead_size;
|
||||
|
||||
if (unlikely(memory_protect_rw(base, usable_size))) {
|
||||
memory_unmap(real, real_alloc_size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (lead_size) {
|
||||
if (unlikely(memory_unmap(real, lead_size))) {
|
||||
memory_unmap(real, real_alloc_size);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (trail_size) {
|
||||
if (unlikely(memory_unmap((char *)base + usable_size + guard_size, trail_size))) {
|
||||
memory_unmap(real, real_alloc_size);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
void deallocate_pages(void *usable, size_t usable_size, size_t guard_size) {
|
||||
if (unlikely(memory_unmap((char *)usable - guard_size, usable_size + guard_size * 2))) {
|
||||
memory_purge(usable, usable_size);
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
#ifndef PAGES_H
|
||||
#define PAGES_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#define PAGE_SHIFT 12
|
||||
#ifndef PAGE_SIZE
|
||||
#define PAGE_SIZE ((size_t)1 << PAGE_SHIFT)
|
||||
#endif
|
||||
|
||||
void *allocate_pages(size_t usable_size, size_t guard_size, bool unprotect, const char *name);
|
||||
void *allocate_pages_aligned(size_t usable_size, size_t alignment, size_t guard_size, const char *name);
|
||||
void deallocate_pages(void *usable, size_t usable_size, size_t guard_size);
|
||||
|
||||
static inline size_t page_align(size_t size) {
|
||||
return align(size, PAGE_SIZE);
|
||||
}
|
||||
|
||||
static inline size_t hash_page(const void *p) {
|
||||
uintptr_t u = (uintptr_t)p >> PAGE_SHIFT;
|
||||
size_t sum = u;
|
||||
sum = (sum << 7) - sum + (u >> 16);
|
||||
sum = (sum << 7) - sum + (u >> 32);
|
||||
sum = (sum << 7) - sum + (u >> 48);
|
||||
return sum;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,6 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
[[ $LD_PRELOAD ]] && LD_PRELOAD+=" "
|
||||
export LD_PRELOAD+="$dir/libhardened_malloc.so"
|
||||
exec "$@"
|
|
@ -1,128 +0,0 @@
|
|||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "chacha.h"
|
||||
#include "random.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <sys/random.h>
|
||||
|
||||
static void get_random_seed(void *buf, size_t size) {
|
||||
while (size) {
|
||||
ssize_t r;
|
||||
|
||||
do {
|
||||
r = getrandom(buf, size, 0);
|
||||
} while (r == -1 && errno == EINTR);
|
||||
|
||||
if (r <= 0) {
|
||||
fatal_error("getrandom failed");
|
||||
}
|
||||
|
||||
buf = (char *)buf + r;
|
||||
size -= r;
|
||||
}
|
||||
}
|
||||
|
||||
void random_state_init(struct random_state *state) {
|
||||
u8 rnd[CHACHA_KEY_SIZE + CHACHA_IV_SIZE];
|
||||
get_random_seed(rnd, sizeof(rnd));
|
||||
chacha_keysetup(&state->ctx, rnd);
|
||||
chacha_ivsetup(&state->ctx, rnd + CHACHA_KEY_SIZE);
|
||||
state->index = RANDOM_CACHE_SIZE;
|
||||
state->reseed = 0;
|
||||
}
|
||||
|
||||
void random_state_init_from_random_state(struct random_state *state, struct random_state *source) {
|
||||
u8 rnd[CHACHA_KEY_SIZE + CHACHA_IV_SIZE];
|
||||
get_random_bytes(source, rnd, sizeof(rnd));
|
||||
chacha_keysetup(&state->ctx, rnd);
|
||||
chacha_ivsetup(&state->ctx, rnd + CHACHA_KEY_SIZE);
|
||||
state->index = RANDOM_CACHE_SIZE;
|
||||
state->reseed = 0;
|
||||
}
|
||||
|
||||
static void refill(struct random_state *state) {
|
||||
if (state->reseed >= RANDOM_RESEED_SIZE) {
|
||||
random_state_init(state);
|
||||
}
|
||||
chacha_keystream_bytes(&state->ctx, state->cache, RANDOM_CACHE_SIZE);
|
||||
state->index = 0;
|
||||
state->reseed += RANDOM_CACHE_SIZE;
|
||||
}
|
||||
|
||||
void get_random_bytes(struct random_state *state, void *buf, size_t size) {
|
||||
// avoid needless copying to and from the cache as an optimization
|
||||
if (size > RANDOM_CACHE_SIZE / 2) {
|
||||
chacha_keystream_bytes(&state->ctx, buf, size);
|
||||
return;
|
||||
}
|
||||
|
||||
while (size) {
|
||||
if (state->index == RANDOM_CACHE_SIZE) {
|
||||
refill(state);
|
||||
}
|
||||
|
||||
size_t remaining = RANDOM_CACHE_SIZE - state->index;
|
||||
size_t copy_size = min(size, remaining);
|
||||
memcpy(buf, state->cache + state->index, copy_size);
|
||||
state->index += copy_size;
|
||||
|
||||
buf = (char *)buf + copy_size;
|
||||
size -= copy_size;
|
||||
}
|
||||
}
|
||||
|
||||
u16 get_random_u16(struct random_state *state) {
|
||||
u16 value;
|
||||
unsigned remaining = RANDOM_CACHE_SIZE - state->index;
|
||||
if (remaining < sizeof(value)) {
|
||||
refill(state);
|
||||
}
|
||||
memcpy(&value, state->cache + state->index, sizeof(value));
|
||||
state->index += sizeof(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
// See Fast Random Integer Generation in an Interval by Daniel Lemire
|
||||
u16 get_random_u16_uniform(struct random_state *state, u16 bound) {
|
||||
u32 random = get_random_u16(state);
|
||||
u32 multiresult = random * bound;
|
||||
u16 leftover = multiresult;
|
||||
if (leftover < bound) {
|
||||
u16 threshold = -bound % bound;
|
||||
while (leftover < threshold) {
|
||||
random = get_random_u16(state);
|
||||
multiresult = random * bound;
|
||||
leftover = (u16)multiresult;
|
||||
}
|
||||
}
|
||||
return multiresult >> 16;
|
||||
}
|
||||
|
||||
u64 get_random_u64(struct random_state *state) {
|
||||
u64 value;
|
||||
unsigned remaining = RANDOM_CACHE_SIZE - state->index;
|
||||
if (remaining < sizeof(value)) {
|
||||
refill(state);
|
||||
}
|
||||
memcpy(&value, state->cache + state->index, sizeof(value));
|
||||
state->index += sizeof(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
// See Fast Random Integer Generation in an Interval by Daniel Lemire
|
||||
u64 get_random_u64_uniform(struct random_state *state, u64 bound) {
|
||||
u128 random = get_random_u64(state);
|
||||
u128 multiresult = random * bound;
|
||||
u64 leftover = multiresult;
|
||||
if (leftover < bound) {
|
||||
u64 threshold = -bound % bound;
|
||||
while (leftover < threshold) {
|
||||
random = get_random_u64(state);
|
||||
multiresult = random * bound;
|
||||
leftover = multiresult;
|
||||
}
|
||||
}
|
||||
return multiresult >> 64;
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
#ifndef RANDOM_H
|
||||
#define RANDOM_H
|
||||
|
||||
#include "chacha.h"
|
||||
#include "util.h"
|
||||
|
||||
#define RANDOM_CACHE_SIZE 256U
|
||||
#define RANDOM_RESEED_SIZE (256U * 1024)
|
||||
|
||||
struct random_state {
|
||||
unsigned index;
|
||||
unsigned reseed;
|
||||
chacha_ctx ctx;
|
||||
u8 cache[RANDOM_CACHE_SIZE];
|
||||
};
|
||||
|
||||
void random_state_init(struct random_state *state);
|
||||
void random_state_init_from_random_state(struct random_state *state, struct random_state *source);
|
||||
void get_random_bytes(struct random_state *state, void *buf, size_t size);
|
||||
u16 get_random_u16(struct random_state *state);
|
||||
u16 get_random_u16_uniform(struct random_state *state, u16 bound);
|
||||
u64 get_random_u64(struct random_state *state);
|
||||
u64 get_random_u64_uniform(struct random_state *state, u64 bound);
|
||||
|
||||
#endif
|
44
src/hardened_malloc/test/.gitignore
vendored
44
src/hardened_malloc/test/.gitignore
vendored
|
@ -1,44 +0,0 @@
|
|||
large_array_growth
|
||||
mallinfo
|
||||
mallinfo2
|
||||
malloc_info
|
||||
offset
|
||||
delete_type_size_mismatch
|
||||
double_free_large
|
||||
double_free_large_delayed
|
||||
double_free_small
|
||||
double_free_small_delayed
|
||||
invalid_free_protected
|
||||
invalid_free_small_region
|
||||
invalid_free_small_region_far
|
||||
invalid_free_unprotected
|
||||
read_after_free_large
|
||||
read_after_free_small
|
||||
read_zero_size
|
||||
string_overflow
|
||||
unaligned_free_large
|
||||
unaligned_free_small
|
||||
uninitialized_free
|
||||
uninitialized_malloc_usable_size
|
||||
uninitialized_realloc
|
||||
write_after_free_large
|
||||
write_after_free_large_reuse
|
||||
write_after_free_small
|
||||
write_after_free_small_reuse
|
||||
write_zero_size
|
||||
unaligned_malloc_usable_size_small
|
||||
invalid_malloc_usable_size_small
|
||||
invalid_malloc_usable_size_small_quarantine
|
||||
malloc_object_size
|
||||
malloc_object_size_offset
|
||||
invalid_malloc_object_size_small
|
||||
invalid_malloc_object_size_small_quarantine
|
||||
impossibly_large_malloc
|
||||
overflow_large_1_byte
|
||||
overflow_large_8_byte
|
||||
overflow_small_1_byte
|
||||
overflow_small_8_byte
|
||||
uninitialized_read_large
|
||||
uninitialized_read_small
|
||||
realloc_init
|
||||
__pycache__/
|
|
@ -1,76 +0,0 @@
|
|||
CONFIG_SLAB_CANARY := true
|
||||
CONFIG_EXTENDED_SIZE_CLASSES := true
|
||||
|
||||
ifneq ($(VARIANT),)
|
||||
$(error testing non-default variants not yet supported)
|
||||
endif
|
||||
|
||||
ifeq (,$(filter $(CONFIG_SLAB_CANARY),true false))
|
||||
$(error CONFIG_SLAB_CANARY must be true or false)
|
||||
endif
|
||||
|
||||
dir=$(dir $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
|
||||
CPPFLAGS := \
|
||||
-D_GNU_SOURCE \
|
||||
-DSLAB_CANARY=$(CONFIG_SLAB_CANARY) \
|
||||
-DCONFIG_EXTENDED_SIZE_CLASSES=$(CONFIG_EXTENDED_SIZE_CLASSES)
|
||||
|
||||
SHARED_FLAGS := -O3
|
||||
|
||||
CFLAGS := -std=c17 $(SHARED_FLAGS) -Wmissing-prototypes
|
||||
CXXFLAGS := -std=c++17 -fsized-deallocation $(SHARED_FLAGS)
|
||||
LDFLAGS := -Wl,-L$(dir)../out,-R,$(dir)../out
|
||||
|
||||
LDLIBS := -lpthread -lhardened_malloc
|
||||
|
||||
EXECUTABLES := \
|
||||
offset \
|
||||
mallinfo \
|
||||
mallinfo2 \
|
||||
malloc_info \
|
||||
large_array_growth \
|
||||
double_free_large \
|
||||
double_free_large_delayed \
|
||||
double_free_small \
|
||||
double_free_small_delayed \
|
||||
unaligned_free_large \
|
||||
unaligned_free_small \
|
||||
read_after_free_large \
|
||||
read_after_free_small \
|
||||
write_after_free_large \
|
||||
write_after_free_large_reuse \
|
||||
write_after_free_small \
|
||||
write_after_free_small_reuse \
|
||||
read_zero_size \
|
||||
write_zero_size \
|
||||
invalid_free_protected \
|
||||
invalid_free_unprotected \
|
||||
invalid_free_small_region \
|
||||
invalid_free_small_region_far \
|
||||
uninitialized_read_small \
|
||||
uninitialized_read_large \
|
||||
uninitialized_free \
|
||||
uninitialized_realloc \
|
||||
uninitialized_malloc_usable_size \
|
||||
overflow_large_1_byte \
|
||||
overflow_large_8_byte \
|
||||
overflow_small_1_byte \
|
||||
overflow_small_8_byte \
|
||||
string_overflow \
|
||||
delete_type_size_mismatch \
|
||||
unaligned_malloc_usable_size_small \
|
||||
invalid_malloc_usable_size_small \
|
||||
invalid_malloc_usable_size_small_quarantine \
|
||||
malloc_object_size \
|
||||
malloc_object_size_offset \
|
||||
invalid_malloc_object_size_small \
|
||||
invalid_malloc_object_size_small_quarantine \
|
||||
impossibly_large_malloc \
|
||||
realloc_init
|
||||
|
||||
all: $(EXECUTABLES)
|
||||
|
||||
clean:
|
||||
rm -f $(EXECUTABLES)
|
||||
rm -fr ./__pycache__
|
|
@ -1,14 +0,0 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
struct foo {
|
||||
uint64_t a, b, c, d;
|
||||
};
|
||||
|
||||
OPTNONE int main(void) {
|
||||
void *p = new char;
|
||||
struct foo *c = (struct foo *)p;
|
||||
delete c;
|
||||
return 0;
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
void *p = malloc(256 * 1024);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
free(p);
|
||||
free(p);
|
||||
return 0;
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
void *p = malloc(256 * 1024);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
void *q = malloc(256 * 1024);
|
||||
if (!q) {
|
||||
return 1;
|
||||
}
|
||||
free(p);
|
||||
free(q);
|
||||
free(p);
|
||||
return 0;
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
void *p = malloc(16);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
free(p);
|
||||
free(p);
|
||||
return 0;
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
void *p = malloc(16);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
void *q = malloc(16);
|
||||
if (!q) {
|
||||
return 1;
|
||||
}
|
||||
free(p);
|
||||
free(q);
|
||||
free(p);
|
||||
return 0;
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(-8);
|
||||
return !(p == NULL);
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
free(malloc(16));
|
||||
char *p = mmap(NULL, 4096 * 16, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
|
||||
if (p == MAP_FAILED) {
|
||||
return 1;
|
||||
}
|
||||
free(p + 4096 * 8);
|
||||
return 0;
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(16);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
char *q = p + 4096 * 4;
|
||||
free(q);
|
||||
return 0;
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(16);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
char *q = p + 1024 * 1024 * 1024;
|
||||
free(q);
|
||||
return 0;
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
free(malloc(16));
|
||||
char *p = mmap(NULL, 4096 * 16, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
|
||||
if (p == MAP_FAILED) {
|
||||
return 1;
|
||||
}
|
||||
free(p + 4096 * 8);
|
||||
return 0;
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
size_t malloc_object_size(void *ptr);
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(16);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
char *q = p + 4096 * 4;
|
||||
malloc_object_size(q);
|
||||
return 0;
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
size_t malloc_object_size(void *ptr);
|
||||
|
||||
OPTNONE int main(void) {
|
||||
void *p = malloc(16);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
free(p);
|
||||
malloc_object_size(p);
|
||||
return 0;
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
#include <malloc.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(16);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
char *q = p + 4096 * 4;
|
||||
malloc_usable_size(q);
|
||||
return 0;
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
#include <malloc.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
void *p = malloc(16);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
free(p);
|
||||
malloc_usable_size(p);
|
||||
return 0;
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
void *p = NULL;
|
||||
size_t size = 256 * 1024;
|
||||
|
||||
for (unsigned i = 0; i < 20; i++) {
|
||||
p = realloc(p, size);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
memset(p, 'a', size);
|
||||
size = size * 3 / 2;
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined(__GLIBC__) || defined(__ANDROID__)
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
static void print_mallinfo(void) {
|
||||
#if defined(__GLIBC__) || defined(__ANDROID__)
|
||||
struct mallinfo info = mallinfo();
|
||||
printf("mallinfo:\n");
|
||||
printf("arena: %zu\n", (size_t)info.arena);
|
||||
printf("ordblks: %zu\n", (size_t)info.ordblks);
|
||||
printf("smblks: %zu\n", (size_t)info.smblks);
|
||||
printf("hblks: %zu\n", (size_t)info.hblks);
|
||||
printf("hblkhd: %zu\n", (size_t)info.hblkhd);
|
||||
printf("usmblks: %zu\n", (size_t)info.usmblks);
|
||||
printf("fsmblks: %zu\n", (size_t)info.fsmblks);
|
||||
printf("uordblks: %zu\n", (size_t)info.uordblks);
|
||||
printf("fordblks: %zu\n", (size_t)info.fordblks);
|
||||
printf("keepcost: %zu\n", (size_t)info.keepcost);
|
||||
#endif
|
||||
}
|
||||
|
||||
OPTNONE int main(void) {
|
||||
void *a[4];
|
||||
|
||||
a[0] = malloc(1024 * 1024 * 1024);
|
||||
a[1] = malloc(16);
|
||||
a[2] = malloc(32);
|
||||
a[3] = malloc(64);
|
||||
|
||||
print_mallinfo();
|
||||
|
||||
free(a[0]);
|
||||
free(a[1]);
|
||||
free(a[2]);
|
||||
free(a[3]);
|
||||
|
||||
printf("\n");
|
||||
print_mallinfo();
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if defined(__GLIBC__)
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
static void print_mallinfo2(void) {
|
||||
#if defined(__GLIBC__)
|
||||
struct mallinfo2 info = mallinfo2();
|
||||
printf("mallinfo2:\n");
|
||||
printf("arena: %zu\n", (size_t)info.arena);
|
||||
printf("ordblks: %zu\n", (size_t)info.ordblks);
|
||||
printf("smblks: %zu\n", (size_t)info.smblks);
|
||||
printf("hblks: %zu\n", (size_t)info.hblks);
|
||||
printf("hblkhd: %zu\n", (size_t)info.hblkhd);
|
||||
printf("usmblks: %zu\n", (size_t)info.usmblks);
|
||||
printf("fsmblks: %zu\n", (size_t)info.fsmblks);
|
||||
printf("uordblks: %zu\n", (size_t)info.uordblks);
|
||||
printf("fordblks: %zu\n", (size_t)info.fordblks);
|
||||
printf("keepcost: %zu\n", (size_t)info.keepcost);
|
||||
#endif
|
||||
}
|
||||
|
||||
OPTNONE int main(void) {
|
||||
void *a[4];
|
||||
|
||||
a[0] = malloc(1024 * 1024 * 1024);
|
||||
a[1] = malloc(16);
|
||||
a[2] = malloc(32);
|
||||
a[3] = malloc(64);
|
||||
|
||||
print_mallinfo2();
|
||||
|
||||
free(a[0]);
|
||||
free(a[1]);
|
||||
free(a[2]);
|
||||
free(a[3]);
|
||||
|
||||
printf("\n");
|
||||
print_mallinfo2();
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined(__GLIBC__) || defined(__ANDROID__)
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
#include "test_util.h"
|
||||
#include "../util.h"
|
||||
|
||||
OPTNONE static void leak_memory(void) {
|
||||
(void)!malloc(1024 * 1024 * 1024);
|
||||
(void)!malloc(16);
|
||||
(void)!malloc(32);
|
||||
(void)!malloc(4096);
|
||||
}
|
||||
|
||||
static void *do_work(UNUSED void *p) {
|
||||
leak_memory();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
pthread_t thread[4];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
pthread_create(&thread[i], NULL, do_work, NULL);
|
||||
}
|
||||
for (int i = 0; i < 4; i++) {
|
||||
pthread_join(thread[i], NULL);
|
||||
}
|
||||
|
||||
#if defined(__GLIBC__) || defined(__ANDROID__)
|
||||
malloc_info(0, stdout);
|
||||
#endif
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
size_t malloc_object_size(void *ptr);
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(16);
|
||||
size_t size = malloc_object_size(p);
|
||||
return size != (SLAB_CANARY ? 24 : 32);
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
size_t malloc_object_size(void *ptr);
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(16);
|
||||
size_t size = malloc_object_size(p + 5);
|
||||
return size != (SLAB_CANARY ? 19 : 27);
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static size_t size_classes[] = {
|
||||
/* large */ 4 * 1024 * 1024,
|
||||
/* 0 */ 0,
|
||||
/* 16 */ 16, 32, 48, 64, 80, 96, 112, 128,
|
||||
/* 32 */ 160, 192, 224, 256,
|
||||
/* 64 */ 320, 384, 448, 512,
|
||||
/* 128 */ 640, 768, 896, 1024,
|
||||
/* 256 */ 1280, 1536, 1792, 2048,
|
||||
/* 512 */ 2560, 3072, 3584, 4096,
|
||||
/* 1024 */ 5120, 6144, 7168, 8192,
|
||||
/* 2048 */ 10240, 12288, 14336, 16384,
|
||||
#if CONFIG_EXTENDED_SIZE_CLASSES
|
||||
/* 4096 */ 20480, 24576, 28672, 32768,
|
||||
/* 8192 */ 40960, 49152, 57344, 65536,
|
||||
/* 16384 */ 81920, 98304, 114688, 131072,
|
||||
#endif
|
||||
};
|
||||
|
||||
#define N_SIZE_CLASSES (sizeof(size_classes) / sizeof(size_classes[0]))
|
||||
|
||||
static const size_t canary_size = SLAB_CANARY ? sizeof(uint64_t) : 0;
|
||||
|
||||
int main(void) {
|
||||
for (unsigned i = 2; i < N_SIZE_CLASSES; i++) {
|
||||
size_classes[i] -= canary_size;
|
||||
}
|
||||
|
||||
void *p[N_SIZE_CLASSES];
|
||||
for (unsigned i = 0; i < N_SIZE_CLASSES; i++) {
|
||||
size_t size = size_classes[i];
|
||||
p[i] = malloc(size);
|
||||
if (!p[i]) {
|
||||
return 1;
|
||||
}
|
||||
void *q = malloc(size);
|
||||
if (!q) {
|
||||
return 1;
|
||||
}
|
||||
if (i != 0) {
|
||||
printf("%zu to %zu: %zd\n", size_classes[i - 1], size, p[i] - p[i - 1]);
|
||||
}
|
||||
printf("%zu to %zu: %zd\n", size, size, q - p[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
#include <malloc.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(256 * 1024);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
size_t size = malloc_usable_size(p);
|
||||
*(p + size) = 0;
|
||||
free(p);
|
||||
return 0;
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
#include <malloc.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(256 * 1024);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
size_t size = malloc_usable_size(p);
|
||||
*(p + size + 7) = 0;
|
||||
free(p);
|
||||
return 0;
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
#include <malloc.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(8);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
size_t size = malloc_usable_size(p);
|
||||
*(p + size) = 1;
|
||||
free(p);
|
||||
return 0;
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
#include <malloc.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(8);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
size_t size = malloc_usable_size(p);
|
||||
// XOR is used to avoid the test having a 1/256 chance to fail
|
||||
*(p + size + 7) ^= 1;
|
||||
free(p);
|
||||
return 0;
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(256 * 1024);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
memset(p, 'a', 16);
|
||||
free(p);
|
||||
for (size_t i = 0; i < 256 * 1024; i++) {
|
||||
printf("%x\n", p[i]);
|
||||
if (p[i] != '\0') {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(16);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
memset(p, 'a', 16);
|
||||
free(p);
|
||||
for (size_t i = 0; i < 16; i++) {
|
||||
printf("%x\n", p[i]);
|
||||
if (p[i] != '\0') {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(0);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
printf("%c\n", *p);
|
||||
return 0;
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static void *thread_func(void *arg) {
|
||||
arg = realloc(arg, 1024);
|
||||
if (!arg) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
free(arg);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
void *mem = realloc(NULL, 12);
|
||||
if (!mem) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
pthread_t thread;
|
||||
int r = pthread_create(&thread, NULL, thread_func, mem);
|
||||
if (r != 0) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
r = pthread_join(thread, NULL);
|
||||
if (r != 0) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <malloc.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(16);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t size = malloc_usable_size(p);
|
||||
memset(p, 'a', size);
|
||||
printf("overflow by %zu bytes\n", strlen(p) - size);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,242 +0,0 @@
|
|||
import os
|
||||
import subprocess
|
||||
import unittest
|
||||
|
||||
|
||||
class TestSimpleMemoryCorruption(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(self):
|
||||
self.dir = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
def run_test(self, test_name):
|
||||
sub = subprocess.Popen(self.dir + "/" + test_name,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
stdout, stderr = sub.communicate()
|
||||
return stdout, stderr, sub.returncode
|
||||
|
||||
def test_delete_type_size_mismatch(self):
|
||||
_stdout, stderr, returncode = self.run_test(
|
||||
"delete_type_size_mismatch")
|
||||
self.assertEqual(returncode, -6)
|
||||
self.assertEqual(stderr.decode(
|
||||
"utf-8"), "fatal allocator error: sized deallocation mismatch (small)\n")
|
||||
|
||||
def test_double_free_large_delayed(self):
|
||||
_stdout, stderr, returncode = self.run_test(
|
||||
"double_free_large_delayed")
|
||||
self.assertEqual(returncode, -6)
|
||||
self.assertEqual(stderr.decode("utf-8"),
|
||||
"fatal allocator error: invalid free\n")
|
||||
|
||||
def test_double_free_large(self):
|
||||
_stdout, stderr, returncode = self.run_test("double_free_large")
|
||||
self.assertEqual(returncode, -6)
|
||||
self.assertEqual(stderr.decode("utf-8"),
|
||||
"fatal allocator error: invalid free\n")
|
||||
|
||||
def test_double_free_small_delayed(self):
|
||||
_stdout, stderr, returncode = self.run_test(
|
||||
"double_free_small_delayed")
|
||||
self.assertEqual(returncode, -6)
|
||||
self.assertEqual(stderr.decode("utf-8"),
|
||||
"fatal allocator error: double free (quarantine)\n")
|
||||
|
||||
def test_double_free_small(self):
|
||||
_stdout, stderr, returncode = self.run_test("double_free_small")
|
||||
self.assertEqual(returncode, -6)
|
||||
self.assertEqual(stderr.decode("utf-8"),
|
||||
"fatal allocator error: double free (quarantine)\n")
|
||||
|
||||
def test_overflow_large_1_byte(self):
|
||||
_stdout, _stderr, returncode = self.run_test(
|
||||
"overflow_large_1_byte")
|
||||
self.assertEqual(returncode, -11)
|
||||
|
||||
def test_overflow_large_8_byte(self):
|
||||
_stdout, _stderr, returncode = self.run_test(
|
||||
"overflow_large_8_byte")
|
||||
self.assertEqual(returncode, -11)
|
||||
|
||||
def test_overflow_small_1_byte(self):
|
||||
_stdout, stderr, returncode = self.run_test(
|
||||
"overflow_small_1_byte")
|
||||
self.assertEqual(returncode, -6)
|
||||
self.assertEqual(stderr.decode("utf-8"),
|
||||
"fatal allocator error: canary corrupted\n")
|
||||
|
||||
def test_overflow_small_8_byte(self):
|
||||
_stdout, stderr, returncode = self.run_test(
|
||||
"overflow_small_8_byte")
|
||||
self.assertEqual(returncode, -6)
|
||||
self.assertEqual(stderr.decode("utf-8"),
|
||||
"fatal allocator error: canary corrupted\n")
|
||||
|
||||
def test_invalid_free_protected(self):
|
||||
_stdout, stderr, returncode = self.run_test("invalid_free_protected")
|
||||
self.assertEqual(returncode, -6)
|
||||
self.assertEqual(stderr.decode("utf-8"),
|
||||
"fatal allocator error: invalid free\n")
|
||||
|
||||
def test_invalid_free_small_region_far(self):
|
||||
_stdout, stderr, returncode = self.run_test(
|
||||
"invalid_free_small_region_far")
|
||||
self.assertEqual(returncode, -6)
|
||||
self.assertEqual(stderr.decode(
|
||||
"utf-8"), "fatal allocator error: invalid free within a slab yet to be used\n")
|
||||
|
||||
def test_invalid_free_small_region(self):
|
||||
_stdout, stderr, returncode = self.run_test(
|
||||
"invalid_free_small_region")
|
||||
self.assertEqual(returncode, -6)
|
||||
self.assertEqual(stderr.decode("utf-8"),
|
||||
"fatal allocator error: double free\n")
|
||||
|
||||
def test_invalid_free_unprotected(self):
|
||||
_stdout, stderr, returncode = self.run_test("invalid_free_unprotected")
|
||||
self.assertEqual(returncode, -6)
|
||||
self.assertEqual(stderr.decode("utf-8"),
|
||||
"fatal allocator error: invalid free\n")
|
||||
|
||||
def test_invalid_malloc_usable_size_small_quarantene(self):
|
||||
_stdout, stderr, returncode = self.run_test(
|
||||
"invalid_malloc_usable_size_small_quarantine")
|
||||
self.assertEqual(returncode, -6)
|
||||
self.assertEqual(stderr.decode(
|
||||
"utf-8"), "fatal allocator error: invalid malloc_usable_size (quarantine)\n")
|
||||
|
||||
def test_invalid_malloc_usable_size_small(self):
|
||||
_stdout, stderr, returncode = self.run_test(
|
||||
"invalid_malloc_usable_size_small")
|
||||
self.assertEqual(returncode, -6)
|
||||
self.assertEqual(stderr.decode(
|
||||
"utf-8"), "fatal allocator error: invalid malloc_usable_size\n")
|
||||
|
||||
def test_read_after_free_large(self):
|
||||
_stdout, _stderr, returncode = self.run_test("read_after_free_large")
|
||||
self.assertEqual(returncode, -11)
|
||||
|
||||
def test_read_after_free_small(self):
|
||||
stdout, _stderr, returncode = self.run_test("read_after_free_small")
|
||||
self.assertEqual(returncode, 0)
|
||||
self.assertEqual(stdout.decode("utf-8"),
|
||||
"0\n0\n0\n0\n0\n0\n0\n0\n0\n0\n0\n0\n0\n0\n0\n0\n")
|
||||
|
||||
def test_read_zero_size(self):
|
||||
_stdout, _stderr, returncode = self.run_test("read_zero_size")
|
||||
self.assertEqual(returncode, -11)
|
||||
|
||||
def test_string_overflow(self):
|
||||
stdout, _stderr, returncode = self.run_test("string_overflow")
|
||||
self.assertEqual(returncode, 0)
|
||||
self.assertEqual(stdout.decode("utf-8"), "overflow by 0 bytes\n")
|
||||
|
||||
def test_unaligned_free_large(self):
|
||||
_stdout, stderr, returncode = self.run_test("unaligned_free_large")
|
||||
self.assertEqual(returncode, -6)
|
||||
self.assertEqual(stderr.decode("utf-8"),
|
||||
"fatal allocator error: invalid free\n")
|
||||
|
||||
def test_unaligned_free_small(self):
|
||||
_stdout, stderr, returncode = self.run_test("unaligned_free_small")
|
||||
self.assertEqual(returncode, -6)
|
||||
self.assertEqual(stderr.decode("utf-8"),
|
||||
"fatal allocator error: invalid unaligned free\n")
|
||||
|
||||
def test_unaligned_malloc_usable_size_small(self):
|
||||
_stdout, stderr, returncode = self.run_test(
|
||||
"unaligned_malloc_usable_size_small")
|
||||
self.assertEqual(returncode, -6)
|
||||
self.assertEqual(stderr.decode("utf-8"),
|
||||
"fatal allocator error: invalid unaligned malloc_usable_size\n")
|
||||
|
||||
def test_uninitialized_free(self):
|
||||
_stdout, stderr, returncode = self.run_test("uninitialized_free")
|
||||
self.assertEqual(returncode, -6)
|
||||
self.assertEqual(stderr.decode("utf-8"),
|
||||
"fatal allocator error: invalid free\n")
|
||||
|
||||
def test_uninitialized_malloc_usable_size(self):
|
||||
_stdout, stderr, returncode = self.run_test(
|
||||
"uninitialized_malloc_usable_size")
|
||||
self.assertEqual(returncode, -6)
|
||||
self.assertEqual(stderr.decode("utf-8"),
|
||||
"fatal allocator error: invalid malloc_usable_size\n")
|
||||
|
||||
def test_uninitialized_realloc(self):
|
||||
_stdout, stderr, returncode = self.run_test("uninitialized_realloc")
|
||||
self.assertEqual(returncode, -6)
|
||||
self.assertEqual(stderr.decode("utf-8"),
|
||||
"fatal allocator error: invalid realloc\n")
|
||||
|
||||
def test_write_after_free_large_reuse(self):
|
||||
_stdout, _stderr, returncode = self.run_test(
|
||||
"write_after_free_large_reuse")
|
||||
self.assertEqual(returncode, -11)
|
||||
|
||||
def test_write_after_free_large(self):
|
||||
_stdout, _stderr, returncode = self.run_test("write_after_free_large")
|
||||
self.assertEqual(returncode, -11)
|
||||
|
||||
def test_write_after_free_small_reuse(self):
|
||||
_stdout, stderr, returncode = self.run_test(
|
||||
"write_after_free_small_reuse")
|
||||
self.assertEqual(returncode, -6)
|
||||
self.assertEqual(stderr.decode("utf-8"),
|
||||
"fatal allocator error: detected write after free\n")
|
||||
|
||||
def test_write_after_free_small(self):
|
||||
_stdout, stderr, returncode = self.run_test("write_after_free_small")
|
||||
self.assertEqual(returncode, -6)
|
||||
self.assertEqual(stderr.decode("utf-8"),
|
||||
"fatal allocator error: detected write after free\n")
|
||||
|
||||
def test_write_zero_size(self):
|
||||
_stdout, _stderr, returncode = self.run_test("write_zero_size")
|
||||
self.assertEqual(returncode, -11)
|
||||
|
||||
def test_malloc_object_size(self):
|
||||
_stdout, _stderr, returncode = self.run_test("malloc_object_size")
|
||||
self.assertEqual(returncode, 0)
|
||||
|
||||
def test_malloc_object_size_offset(self):
|
||||
_stdout, _stderr, returncode = self.run_test(
|
||||
"malloc_object_size_offset")
|
||||
self.assertEqual(returncode, 0)
|
||||
|
||||
def test_invalid_malloc_object_size_small(self):
|
||||
_stdout, stderr, returncode = self.run_test(
|
||||
"invalid_malloc_object_size_small")
|
||||
self.assertEqual(returncode, -6)
|
||||
self.assertEqual(stderr.decode(
|
||||
"utf-8"), "fatal allocator error: invalid malloc_object_size\n")
|
||||
|
||||
def test_invalid_malloc_object_size_small_quarantine(self):
|
||||
_stdout, stderr, returncode = self.run_test(
|
||||
"invalid_malloc_object_size_small_quarantine")
|
||||
self.assertEqual(returncode, -6)
|
||||
self.assertEqual(stderr.decode(
|
||||
"utf-8"), "fatal allocator error: invalid malloc_object_size (quarantine)\n")
|
||||
|
||||
def test_impossibly_large_malloc(self):
|
||||
_stdout, stderr, returncode = self.run_test(
|
||||
"impossibly_large_malloc")
|
||||
self.assertEqual(returncode, 0)
|
||||
|
||||
def test_uninitialized_read_small(self):
|
||||
_stdout, stderr, returncode = self.run_test(
|
||||
"uninitialized_read_small")
|
||||
self.assertEqual(returncode, 0)
|
||||
|
||||
def test_uninitialized_read_large(self):
|
||||
_stdout, stderr, returncode = self.run_test(
|
||||
"uninitialized_read_large")
|
||||
self.assertEqual(returncode, 0)
|
||||
|
||||
def test_realloc_init(self):
|
||||
_stdout, _stderr, returncode = self.run_test(
|
||||
"realloc_init")
|
||||
self.assertEqual(returncode, 0)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
|
@ -1,10 +0,0 @@
|
|||
#ifndef TEST_UTIL_H
|
||||
#define TEST_UTIL_H
|
||||
|
||||
#ifdef __clang__
|
||||
#define OPTNONE __attribute__((optnone))
|
||||
#else
|
||||
#define OPTNONE __attribute__((optimize(0)))
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -1,12 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(256 * 1024);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
free(p + 1);
|
||||
return 0;
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(16);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
free(p + 1);
|
||||
return 0;
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
#include <malloc.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(16);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
malloc_usable_size(p + 1);
|
||||
return 0;
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
free((void *)1);
|
||||
return 0;
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
#include <malloc.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
malloc_usable_size((void *)1);
|
||||
return 0;
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(256 * 1024);
|
||||
for (unsigned i = 0; i < 256 * 1024; i++) {
|
||||
if (p[i] != 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
free(p);
|
||||
return 0;
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(8);
|
||||
for (unsigned i = 0; i < 8; i++) {
|
||||
if (p[i] != 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
free(p);
|
||||
return 0;
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
void *p = realloc((void *)1, 16);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(256 * 1024);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
free(p);
|
||||
p[64 * 1024 + 1] = 'a';
|
||||
return 0;
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "test_util.h"
|
||||
#include "../util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(256 * 1024);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
free(p);
|
||||
UNUSED char *q = malloc(256 * 1024);
|
||||
p[64 * 1024 + 1] = 'a';
|
||||
return 0;
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(128);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
free(p);
|
||||
|
||||
p[65] = 'a';
|
||||
|
||||
// trigger reuse of the allocation
|
||||
for (size_t i = 0; i < 100000; i++) {
|
||||
free(malloc(128));
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "test_util.h"
|
||||
#include "../util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(128);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
free(p);
|
||||
UNUSED char *q = malloc(128);
|
||||
|
||||
p[65] = 'a';
|
||||
|
||||
// trigger reuse of the allocation
|
||||
for (size_t i = 0; i < 100000; i++) {
|
||||
free(malloc(128));
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(0);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
*p = 5;
|
||||
return 0;
|
||||
}
|
3126
src/hardened_malloc/third_party/libdivide.h
vendored
3126
src/hardened_malloc/third_party/libdivide.h
vendored
File diff suppressed because it is too large
Load diff
|
@ -1,41 +0,0 @@
|
|||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <async_safe/log.h>
|
||||
#endif
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#ifndef __ANDROID__
|
||||
static int write_full(int fd, const char *buf, size_t length) {
|
||||
do {
|
||||
ssize_t bytes_written = write(fd, buf, length);
|
||||
if (bytes_written == -1) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
buf += bytes_written;
|
||||
length -= bytes_written;
|
||||
} while (length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
COLD noreturn void fatal_error(const char *s) {
|
||||
#ifdef __ANDROID__
|
||||
async_safe_fatal("hardened_malloc: fatal allocator error: %s", s);
|
||||
#else
|
||||
const char *prefix = "fatal allocator error: ";
|
||||
(void)(write_full(STDERR_FILENO, prefix, strlen(prefix)) != -1 &&
|
||||
write_full(STDERR_FILENO, s, strlen(s)) != -1 &&
|
||||
write_full(STDERR_FILENO, "\n", 1));
|
||||
abort();
|
||||
#endif
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// C11 noreturn doesn't work in C++
|
||||
#define noreturn __attribute__((noreturn))
|
||||
|
||||
#define likely(x) __builtin_expect(!!(x), 1)
|
||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||
|
||||
#define min(x, y) ({ \
|
||||
__typeof__(x) _x = (x); \
|
||||
__typeof__(y) _y = (y); \
|
||||
(void) (&_x == &_y); \
|
||||
_x < _y ? _x : _y; })
|
||||
|
||||
#define max(x, y) ({ \
|
||||
__typeof__(x) _x = (x); \
|
||||
__typeof__(y) _y = (y); \
|
||||
(void) (&_x == &_y); \
|
||||
_x > _y ? _x : _y; })
|
||||
|
||||
#define COLD __attribute__((cold))
|
||||
#define UNUSED __attribute__((unused))
|
||||
#define EXPORT __attribute__((visibility("default")))
|
||||
|
||||
#define STRINGIFY(s) #s
|
||||
#define ALIAS(f) __attribute__((alias(STRINGIFY(f))))
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
typedef unsigned __int128 u128;
|
||||
|
||||
#define U64_WIDTH 64
|
||||
|
||||
static inline int ffz64(u64 x) {
|
||||
return __builtin_ffsll(~x);
|
||||
}
|
||||
|
||||
// parameter must not be 0
|
||||
static inline int clz64(u64 x) {
|
||||
return __builtin_clzll(x);
|
||||
}
|
||||
|
||||
// parameter must not be 0
|
||||
static inline u64 log2u64(u64 x) {
|
||||
return U64_WIDTH - clz64(x) - 1;
|
||||
}
|
||||
|
||||
static inline size_t align(size_t size, size_t align) {
|
||||
size_t mask = align - 1;
|
||||
return (size + mask) & ~mask;
|
||||
}
|
||||
|
||||
// u4_arr_{set,get} are helper functions for using u8 array as an array of unsigned 4-bit values.
|
||||
|
||||
// val is treated as a 4-bit value
|
||||
static inline void u4_arr_set(u8 *arr, size_t idx, u8 val) {
|
||||
size_t off = idx >> 1;
|
||||
size_t shift = (idx & 1) << 2;
|
||||
u8 mask = (u8) (0xf0 >> shift);
|
||||
arr[off] = (arr[off] & mask) | (val << shift);
|
||||
}
|
||||
|
||||
static inline u8 u4_arr_get(const u8 *arr, size_t idx) {
|
||||
size_t off = idx >> 1;
|
||||
size_t shift = (idx & 1) << 2;
|
||||
return (u8) ((arr[off] >> shift) & 0xf);
|
||||
}
|
||||
|
||||
COLD noreturn void fatal_error(const char *s);
|
||||
|
||||
#if CONFIG_SEAL_METADATA
|
||||
|
||||
#ifdef __GLIBC__
|
||||
#define USE_PKEY
|
||||
#else
|
||||
#error "CONFIG_SEAL_METADATA requires Memory Protection Key support"
|
||||
#endif
|
||||
|
||||
#endif // CONFIG_SEAL_METADATA
|
||||
|
||||
#endif
|
Loading…
Add table
Reference in a new issue