mirror of
https://github.com/GrapheneOS/hardened_malloc.git
synced 2025-04-19 22:10:19 +02:00
Compare commits
435 commits
PQ2A.19030
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
7481c8857f | ||
|
1d7fc7ffe0 | ||
|
4fe9018b6f | ||
|
3ab23f7ebf | ||
|
c894f3ec1d | ||
|
c97263ef0c | ||
|
a7302add63 | ||
|
b1d9571fec | ||
|
e03579253a | ||
|
9739cb4690 | ||
|
aa950244f8 | ||
|
6402e2b0d4 | ||
|
e86192e7fe | ||
|
6ce663a8bd | ||
|
9ca9d2d925 | ||
|
3f07acfab1 | ||
|
749640c274 | ||
|
7268189933 | ||
|
3c1f40aff0 | ||
|
5fbbdc2ef8 | ||
|
7d2151e40c | ||
|
4756716904 | ||
|
a3bf742c3e | ||
|
53a45b4661 | ||
|
abe54dba27 | ||
|
365ee6900d | ||
|
7093fdc482 | ||
|
61821b02c8 | ||
|
3c274731ba | ||
|
4171bd164e | ||
|
352c083f65 | ||
|
88b3c1acf9 | ||
|
f793a3edf6 | ||
|
fd75fc1ba8 | ||
|
72dc236d5f | ||
|
be08eeee2d | ||
|
25f0fe9c69 | ||
|
c75cb4c3f3 | ||
|
b560431c01 | ||
|
009f2dad76 | ||
|
03883eb2ce | ||
|
7a6dbd8152 | ||
|
f16ef601d4 | ||
|
155800526a | ||
|
28d5d394cf | ||
|
577d9583eb | ||
|
93aa9eefe4 | ||
|
01a199e19e | ||
|
576328b1b4 | ||
|
5137d2da4d | ||
|
f042a6b9b0 | ||
|
001fc86585 | ||
|
70c91f4c3e | ||
|
e3686ae457 | ||
|
19a46e0f96 | ||
|
8d5c631224 | ||
|
903cba5a84 | ||
|
9cb4e6daf6 | ||
|
8696431b88 | ||
|
2d302f7d85 | ||
|
d5f9909eca | ||
|
5e1901e85d | ||
|
462c2c5293 | ||
|
8f3281ed6a | ||
|
7d75acc62a | ||
|
af866a7faa | ||
|
64dad0a69f | ||
|
95c4b40caf | ||
|
cc70583beb | ||
|
62a98efb13 | ||
|
d3152b8e8f | ||
|
2e9daf3122 | ||
|
6038030d0b | ||
|
4d23fa37ad | ||
|
6d36e758f5 | ||
|
cd9b875297 | ||
|
2250130c53 | ||
|
72dba6765f | ||
|
8f38bbdee6 | ||
|
dd427cb3b8 | ||
|
b5dd9d11d9 | ||
|
72fb3576f5 | ||
|
f8fec401c7 | ||
|
0d6d63cbe7 | ||
|
8fd31e4bc1 | ||
|
b511696c55 | ||
|
943704de7c | ||
|
04a86566c3 | ||
|
448170a412 | ||
|
995ce07d45 | ||
|
c9d1abcd7e | ||
|
8f0b252c33 | ||
|
3cffc1e1af | ||
|
ae2524bf88 | ||
|
e28addda19 | ||
|
9d89712386 | ||
|
84eadd8568 | ||
|
0bbcc5d610 | ||
|
3fa30842ed | ||
|
b3d78bd5f6 | ||
|
8d61e63274 | ||
|
422ee78b3e | ||
|
3e312695e1 | ||
|
81cf2f27a0 | ||
|
d8cb2d9f7a | ||
|
86f9c739ee | ||
|
536f852538 | ||
|
e814cf4f5c | ||
|
705211ef49 | ||
|
189d3362d5 | ||
|
e2bcf4a356 | ||
|
d470ae56a5 | ||
|
42b097f3b0 | ||
|
17891d743e | ||
|
efd71e70c7 | ||
|
a6d27848af | ||
|
110126d7f0 | ||
|
a2bdb4da27 | ||
|
0c0561e563 | ||
|
5a577e9ee0 | ||
|
b3372e1576 | ||
|
052b756840 | ||
|
001eb0687b | ||
|
2a5662948e | ||
|
d1c39edc9b | ||
|
aa1746a90d | ||
|
f3efc26638 | ||
|
78cbb964d4 | ||
|
36dfed3354 | ||
|
8a500088c6 | ||
|
c50d06bc6a | ||
|
645414cc9f | ||
|
13a1f578cb | ||
|
acda766e2c | ||
|
5f32942263 | ||
|
346529574d | ||
|
16c991b8f7 | ||
|
5f59ee3935 | ||
|
3696f071a4 | ||
|
7d6663ed80 | ||
|
c6af50d088 | ||
|
8ae78237ae | ||
|
3f8e9d3184 | ||
|
1e526fc36b | ||
|
c5be4b1888 | ||
|
ffdf7b1ee1 | ||
|
2d56c1de01 | ||
|
3878f4a5f4 | ||
|
de7a3b6e5a | ||
|
9142a9376b | ||
|
75e26afdb6 | ||
|
cff1d6d4b5 | ||
|
75952581ee | ||
|
a84d3f5310 | ||
|
0655c1d024 | ||
|
2b25c791ee | ||
|
e816c545ea | ||
|
06192ae499 | ||
|
4ccd6f16df | ||
|
9966adbdad | ||
|
769e01fc4b | ||
|
460fef456d | ||
|
1a650b0317 | ||
|
fa46a7a85d | ||
|
d8817417cc | ||
|
7106bff27f | ||
|
1bdbb2d3f7 | ||
|
a33d2ca97d | ||
|
aa94408cc2 | ||
|
8f9305df57 | ||
|
cc0a1e1736 | ||
|
3b72a4f810 | ||
|
e41d37c3de | ||
|
23969727d8 | ||
|
4d30b491e3 | ||
|
11207a9c98 | ||
|
801e8d959f | ||
|
8dfea34fc0 | ||
|
4d6456cf58 | ||
|
be6dde66f9 | ||
|
e0ecacff45 | ||
|
050871122b | ||
|
27fcfccb67 | ||
|
93a87ce30b | ||
|
da190f1469 | ||
|
b0f81365a8 | ||
|
c9820b6e37 | ||
|
f1cdc1e484 | ||
|
26b74b87bf | ||
|
89faba4232 | ||
|
a45dacc57b | ||
|
a71ab1a2eb | ||
|
96a322bcbe | ||
|
92a1e456d2 | ||
|
9706f5a311 | ||
|
440489af67 | ||
|
f9a8e7216b | ||
|
5c974bdf82 | ||
|
2335f56713 | ||
|
13a3aa16d0 | ||
|
8bfa1a7dd5 | ||
|
3952645318 | ||
|
1d15d34c7e | ||
|
29ffcdf810 | ||
|
f773a96b59 | ||
|
b84af9b499 | ||
|
73b78a8adb | ||
|
e77ffa76d9 | ||
|
86b0b3e452 | ||
|
7b03b5c629 | ||
|
db21ecd529 | ||
|
ee55acf116 | ||
|
a3b4c163eb | ||
|
325b82f1bd | ||
|
ddd14bc421 | ||
|
29b09648d6 | ||
|
1984cb3b3d | ||
|
76860c72e1 | ||
|
5c8b686370 | ||
|
5275563252 | ||
|
e9d9f70ad4 | ||
|
10c5d61187 | ||
|
b90f650153 | ||
|
8d0314295e | ||
|
b072022022 | ||
|
2bb1c39d31 | ||
|
0bf18b7c26 | ||
|
178d4f320f | ||
|
b9ebf47c7c | ||
|
8906c0941a | ||
|
59e174eee0 | ||
|
483b1d7b8b | ||
|
96eca21ac5 | ||
|
022b64791e | ||
|
b4bbd09f07 | ||
|
a88305c01b | ||
|
85c5c3736c | ||
|
96a9bcf3a1 | ||
|
41fb89517a | ||
|
50e0f1334c | ||
|
9fb2791af2 | ||
|
8974af86d1 | ||
|
d203d6c445 | ||
|
9f5e1f6eb9 | ||
|
1cba254452 | ||
|
730f148647 | ||
|
dd7291ebfe | ||
|
bcb93cab63 | ||
|
f214bd541a | ||
|
7804e263e9 | ||
|
de3fb50dcc | ||
|
b404d6da6e | ||
|
c9c7dca498 | ||
|
dcc80a01db | ||
|
722974f4e9 | ||
|
195bc8c92a | ||
|
577524798e | ||
|
467ba8440f | ||
|
067b3c864f | ||
|
82440e78d9 | ||
|
fc0bd78215 | ||
|
08a5f5ee0b | ||
|
e82367e1bf | ||
|
4a6bbe445c | ||
|
cf55ac0f6d | ||
|
b672316bc7 | ||
|
029a2edf28 | ||
|
35bd7cd76d | ||
|
0a3a726c93 | ||
|
19365c25d6 | ||
|
c75dcb9d9c | ||
|
d757835d90 | ||
|
2c421590b5 | ||
|
9f661f945c | ||
|
b160f723e0 | ||
|
ec6854c71b | ||
|
6b987e644b | ||
|
2a87f52fc1 | ||
|
466d351e93 | ||
|
0436227092 | ||
|
3af44d2e6a | ||
|
a5abe5add6 | ||
|
26134f9aaa | ||
|
449962e044 | ||
|
bee398f860 | ||
|
7c5c768e2f | ||
|
7945b3f109 | ||
|
dfa49481e5 | ||
|
2fbf7bb25e | ||
|
ac95820fae | ||
|
b48ac93b03 | ||
|
eff5037d64 | ||
|
97ea85f55d | ||
|
6f4de3971e | ||
|
cb73bccf35 | ||
|
74eb614f16 | ||
|
a28da3c65a | ||
|
fb9f5d630b | ||
|
8d648e2b25 | ||
|
6d78dec42a | ||
|
2e4ab73fb6 | ||
|
0e4ea0090b | ||
|
5b3d59ec7d | ||
|
7c455c3956 | ||
|
efda950994 | ||
|
40be77003e | ||
|
0af33616f0 | ||
|
c66cf10894 | ||
|
0129d8e470 | ||
|
5eefcd39b4 | ||
|
2288b3a754 | ||
|
f4afedb137 | ||
|
ac70e2c250 | ||
|
d0b466beb8 | ||
|
7a8c57d0f5 | ||
|
c4fc025fde | ||
|
58b56f10ea | ||
|
125efe99db | ||
|
77b242ea3f | ||
|
d37657e125 | ||
|
3c67708c3a | ||
|
2ad74515b1 | ||
|
abece7656b | ||
|
c70745ab15 | ||
|
7d4d2ef0fb | ||
|
8133444f43 | ||
|
8f9f2521a0 | ||
|
d8ebdea05f | ||
|
defd55f302 | ||
|
04f69d9f0d | ||
|
995d0580d1 | ||
|
8d2df1deb8 | ||
|
1bc201c4c1 | ||
|
cc8c4459e1 | ||
|
b6b910f032 | ||
|
24de5aab05 | ||
|
71e4577367 | ||
|
75e86914aa | ||
|
90d12fb340 | ||
|
77743e5a36 | ||
|
3ed6e546c8 | ||
|
d80919fa1e | ||
|
410e9efb93 | ||
|
7bcfa500be | ||
|
72a08f88fb | ||
|
a32e26b8e9 | ||
|
934ab4cb59 | ||
|
060f74b993 | ||
|
4d4277319a | ||
|
a579257a26 | ||
|
bb65d088dc | ||
|
706c1970b5 | ||
|
dba11c0091 | ||
|
539d4f0d37 | ||
|
bc75c4db7b | ||
|
37474e117c | ||
|
12525f2861 | ||
|
5449f4a94e | ||
|
64a1f59020 | ||
|
b40ba9754b | ||
|
f6f4402ff3 | ||
|
2ae0ed4674 | ||
|
ae4142c2d1 | ||
|
18f36c3e8d | ||
|
7f0bbddfca | ||
|
409a639312 | ||
|
494436c904 | ||
|
13ee04c8c3 | ||
|
a13db3fc68 | ||
|
c7e2cb82f4 | ||
|
f115be8392 | ||
|
e7eeb3f35c | ||
|
7e465c621e | ||
|
1c899657c1 | ||
|
8774065b13 | ||
|
84a25ec83e | ||
|
34b6754f70 | ||
|
d4b8fee1c4 | ||
|
086eb1fee4 | ||
|
7a89a7b8c5 | ||
|
b31e8dacb1 | ||
|
6c31f6710a | ||
|
1b34fd4a69 | ||
|
d5f18c47b3 | ||
|
62c73d8b41 | ||
|
d5c00b4d0d | ||
|
d5c1bca915 | ||
|
9a0de626fc | ||
|
02bfcc3b75 | ||
|
cef7368b3c | ||
|
295bfbde94 | ||
|
8b2b9d940a | ||
|
9453332e57 | ||
|
922c741915 | ||
|
a4cff7a960 | ||
|
ef90f404a6 | ||
|
e0891c8cfc | ||
|
7a7126e780 | ||
|
c68de6141d | ||
|
ce36d0c826 | ||
|
3d18fb8074 | ||
|
4f08e40fe5 | ||
|
55891357ff | ||
|
0651c819e3 | ||
|
491ce6b0b1 | ||
|
1eed432b9a | ||
|
ec8fb347ed | ||
|
beaa39a5a3 | ||
|
27a4c883ce | ||
|
e94fe50a0d | ||
|
712748aaa8 | ||
|
0f107cd2a3 | ||
|
350d0e5fd2 | ||
|
7acebaa837 | ||
|
df9650fe64 | ||
|
98deb9de52 | ||
|
fc8f2c3b60 | ||
|
b5187a0aff | ||
|
494cc5ec50 | ||
|
c5e911419d | ||
|
55769496dc | ||
|
13de480bde | ||
|
3d142eb4c2 | ||
|
64dfd23f7b | ||
|
178ec6e3f9 | ||
|
6e67106882 | ||
|
1d62075291 | ||
|
c8b948e202 | ||
|
a0ee5f445b | ||
|
45337ebe07 | ||
|
65311a5df2 | ||
|
e4120913d6 | ||
|
87acbcdf23 | ||
|
ddd616aa0b | ||
|
4a000d96e2 |
87 changed files with 6572 additions and 2325 deletions
2
.clang-tidy
Normal file
2
.clang-tidy
Normal file
|
@ -0,0 +1,2 @@
|
|||
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
.github/dependabot.yml
vendored
Normal file
7
.github/dependabot.yml
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
target-branch: main
|
55
.github/workflows/build-and-test.yml
vendored
Normal file
55
.github/workflows/build-and-test.yml
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
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, 13, 14]
|
||||
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, 16, 17, 18]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install dependencies
|
||||
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends clang-14 clang-15
|
||||
- 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
|
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1,2 +1,2 @@
|
|||
*.o
|
||||
*.so
|
||||
out/
|
||||
out-light/
|
||||
|
|
46
Android.bp
46
Android.bp
|
@ -1,15 +1,14 @@
|
|||
common_cflags = [
|
||||
"-pipe",
|
||||
"-O3",
|
||||
//"-flto",
|
||||
"-fPIC",
|
||||
"-fvisibility=hidden",
|
||||
//"-fno-plt",
|
||||
"-pipe",
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Wcast-align",
|
||||
"-Wcast-qual",
|
||||
"-Wwrite-strings",
|
||||
"-Werror",
|
||||
"-DH_MALLOC_PREFIX",
|
||||
"-DZERO_ON_FREE=true",
|
||||
"-DWRITE_AFTER_FREE_CHECK=true",
|
||||
|
@ -17,21 +16,25 @@ common_cflags = [
|
|||
"-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=128",
|
||||
"-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=1073741824", // 1GiB
|
||||
"-DLABEL_MEMORY",
|
||||
"-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=c11", "-Wmissing-prototypes"],
|
||||
conlyflags: ["-std=c17", "-Wmissing-prototypes"],
|
||||
stl: "none",
|
||||
}
|
||||
|
||||
|
@ -44,8 +47,35 @@ lib_src_files = [
|
|||
"util.c",
|
||||
]
|
||||
|
||||
cc_library_static {
|
||||
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=armv8-a+dotprod+memtag"]
|
||||
},
|
||||
},
|
||||
apex_available: [
|
||||
"com.android.runtime",
|
||||
],
|
||||
}
|
||||
|
|
232
CREDITS
232
CREDITS
|
@ -4,7 +4,7 @@ chacha.c is a simple conversion of chacha-merged.c to a keystream-only implement
|
|||
D. J. Bernstein
|
||||
Public domain.
|
||||
|
||||
malloc.c open-addressed hash table (regions_grow, regions_insert, regions_find, regions_delete):
|
||||
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>
|
||||
|
@ -25,7 +25,8 @@ malloc.c open-addressed hash table (regions_grow, regions_insert, regions_find,
|
|||
|
||||
libdivide:
|
||||
|
||||
Copyright (C) 2010 ridiculous_fish
|
||||
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
|
||||
|
||||
|
@ -53,3 +54,230 @@ libdivide:
|
|||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
|
|
|
@ -16,6 +16,8 @@ Somewhat important and an easy sell:
|
|||
* 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:
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2019 Daniel Micay
|
||||
Copyright © 2018-2025 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
|
||||
|
|
131
Makefile
131
Makefile
|
@ -1,53 +1,60 @@
|
|||
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_GUARD_SLABS_INTERVAL := 1
|
||||
CONFIG_GUARD_SIZE_DIVISOR := 2
|
||||
CONFIG_REGION_QUARANTINE_RANDOM_LENGTH := 128
|
||||
CONFIG_REGION_QUARANTINE_QUEUE_LENGTH := 1024
|
||||
CONFIG_REGION_QUARANTINE_SKIP_THRESHOLD := 33554432 # 32MiB
|
||||
CONFIG_FREE_SLABS_QUARANTINE_RANDOM_LENGTH := 32
|
||||
CONFIG_CLASS_REGION_SIZE := 137438953472 # 128GiB
|
||||
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) -E $1 - </dev/null >/dev/null 2>&1 && echo $1)
|
||||
$(shell $(CC) $(if $(filter clang%,$(CC)),-Werror=unknown-warning-option) -E $1 - </dev/null >/dev/null 2>&1 && echo $1 || echo $2)
|
||||
endef
|
||||
|
||||
CPPFLAGS := -D_GNU_SOURCE
|
||||
SHARED_FLAGS := -O3 -flto -fPIC -fvisibility=hidden -fno-plt -pipe -Wall -Wextra $(call safe_flag,-Wcast-align=strict) -Wcast-qual -Wwrite-strings
|
||||
CPPFLAGS := $(CPPFLAGS) -D_GNU_SOURCE -I include
|
||||
SHARED_FLAGS := -pipe -O3 -flto -fPIC -fvisibility=hidden -fno-plt \
|
||||
-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
|
||||
|
||||
CFLAGS := -std=c11 $(SHARED_FLAGS) -Wmissing-prototypes
|
||||
CXXFLAGS := -std=c++14 $(SHARED_FLAGS)
|
||||
LDFLAGS := -Wl,--as-needed,-z,defs,-z,relro,-z,now,-z,nodlopen,-z,text
|
||||
TIDY_CHECKS := -checks=bugprone-*,-bugprone-macro-parentheses,cert-*,clang-analyzer-*,readability-*,-readability-inconsistent-declaration-parameter-name,-readability-named-parameter
|
||||
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
|
||||
|
||||
ifeq ($(CONFIG_UBSAN),true)
|
||||
CFLAGS += -fsanitize=undefined
|
||||
CXXFLAGS += -fsanitize=undefined
|
||||
endif
|
||||
OBJECTS := $(addprefix $(OUT)/,$(OBJECTS))
|
||||
|
||||
ifeq ($(CONFIG_SEAL_METADATA),true)
|
||||
CPPFLAGS += -DCONFIG_SEAL_METADATA
|
||||
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))
|
||||
|
@ -66,36 +73,76 @@ 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)
|
||||
-DCONFIG_CLASS_REGION_SIZE=$(CONFIG_CLASS_REGION_SIZE) \
|
||||
-DN_ARENA=$(CONFIG_N_ARENA) \
|
||||
-DCONFIG_STATS=$(CONFIG_STATS) \
|
||||
-DCONFIG_SELF_INIT=$(CONFIG_SELF_INIT)
|
||||
|
||||
libhardened_malloc.so: $(OBJECTS)
|
||||
$(OUT)/libhardened_malloc$(SUFFIX).so: $(OBJECTS) | $(OUT)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -shared $^ $(LDLIBS) -o $@
|
||||
|
||||
chacha.o: chacha.c chacha.h util.h
|
||||
h_malloc.o: h_malloc.c h_malloc.h mutex.h memory.h pages.h random.h util.h
|
||||
memory.o: memory.c memory.h util.h
|
||||
new.o: new.cc h_malloc.h util.h
|
||||
pages.o: pages.c pages.h memory.h util.h
|
||||
random.o: random.c random.h chacha.h util.h
|
||||
util.o: util.c util.h
|
||||
$(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 $(TIDY_CHECKS) $(SOURCES) -- $(CPPFLAGS)
|
||||
clang-tidy --extra-arg=-std=c17 $(filter %.c,$(SOURCES)) -- $(CPPFLAGS)
|
||||
clang-tidy --extra-arg=-std=c++17 $(filter %.cc,$(SOURCES)) -- $(CPPFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f libhardened_malloc.so $(OBJECTS)
|
||||
rm -f $(OUT)/libhardened_malloc.so $(OBJECTS)
|
||||
$(MAKE) -C test/ clean
|
||||
|
||||
.PHONY: clean tidy
|
||||
test: $(OUT)/libhardened_malloc$(SUFFIX).so
|
||||
$(MAKE) -C test/
|
||||
python3 -m unittest discover --start-directory test/
|
||||
|
||||
.PHONY: check clean tidy test
|
||||
|
|
25
androidtest/Android.bp
Normal file
25
androidtest/Android.bp
Normal file
|
@ -0,0 +1,25 @@
|
|||
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",
|
||||
],
|
||||
}
|
13
androidtest/AndroidTest.xml
Normal file
13
androidtest/AndroidTest.xml
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?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>
|
17
androidtest/memtag/Android.bp
Normal file
17
androidtest/memtag/Android.bp
Normal file
|
@ -0,0 +1,17 @@
|
|||
cc_test {
|
||||
name: "memtag_test",
|
||||
srcs: ["memtag_test.cc"],
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
"-Wextra",
|
||||
"-O0",
|
||||
"-march=armv9-a+memtag",
|
||||
],
|
||||
|
||||
compile_multilib: "64",
|
||||
|
||||
sanitize: {
|
||||
memtag_heap: true,
|
||||
},
|
||||
}
|
351
androidtest/memtag/memtag_test.cc
Normal file
351
androidtest/memtag/memtag_test.cc
Normal file
|
@ -0,0 +1,351 @@
|
|||
// 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/mman.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "../../arm_mte.h"
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void *set_pointer_tag(void *ptr, u8 tag) {
|
||||
return (void *) (((uintptr_t) tag << 56) | (uintptr_t) untag_pointer(ptr));
|
||||
}
|
||||
|
||||
// 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() {
|
||||
// tag 0 is reserved
|
||||
const int min_tag = 1;
|
||||
const int max_tag = 0xf;
|
||||
|
||||
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 for the reserved tag 0
|
||||
assert(seen_tags == (0xffff & ~(1 << 0)));
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
int expected_segv_code;
|
||||
|
||||
#define expect_segv(exp, segv_code) ({\
|
||||
expected_segv_code = segv_code; \
|
||||
volatile auto val = exp; \
|
||||
(void) val; \
|
||||
do_context_switch(); \
|
||||
fprintf(stderr, "didn't receive SEGV code %i", segv_code); \
|
||||
exit(1); })
|
||||
|
||||
// it's expected that the device is configured to use asymm MTE tag checking mode (sync read checks,
|
||||
// async write checks)
|
||||
#define expect_read_segv(exp) expect_segv(exp, SEGV_MTESERR)
|
||||
#define expect_write_segv(exp) expect_segv(exp, SEGV_MTEAERR)
|
||||
|
||||
void read_after_free() {
|
||||
u8 *p = alloc_default();
|
||||
free(p);
|
||||
expect_read_segv(p[0]);
|
||||
}
|
||||
|
||||
void write_after_free() {
|
||||
u8 *p = alloc_default();
|
||||
free(p);
|
||||
expect_write_segv(p[0] = 1);
|
||||
}
|
||||
|
||||
void underflow_read() {
|
||||
u8 *p = alloc_default();
|
||||
expect_read_segv(p[-1]);
|
||||
}
|
||||
|
||||
void underflow_write() {
|
||||
u8 *p = alloc_default();
|
||||
expect_write_segv(p[-1] = 1);
|
||||
}
|
||||
|
||||
void overflow_read() {
|
||||
u8 *p = alloc_default();
|
||||
expect_read_segv(p[DEFAULT_ALLOC_SIZE + CANARY_SIZE]);
|
||||
}
|
||||
|
||||
void overflow_write() {
|
||||
u8 *p = alloc_default();
|
||||
expect_write_segv(p[DEFAULT_ALLOC_SIZE + CANARY_SIZE] = 1);
|
||||
}
|
||||
|
||||
void untagged_read() {
|
||||
u8 *p = alloc_default();
|
||||
p = (u8 *) untag_pointer(p);
|
||||
expect_read_segv(p[0]);
|
||||
}
|
||||
|
||||
void untagged_write() {
|
||||
u8 *p = alloc_default();
|
||||
p = (u8 *) untag_pointer(p);
|
||||
expect_write_segv(p[0] = 1);
|
||||
}
|
||||
|
||||
// checks that each of memory locations inside the buffer is tagged with expected_tag
|
||||
void check_tag(void *buf, size_t len, u8 expected_tag) {
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
assert(get_pointer_tag(__arm_mte_get_tag((void *) ((uintptr_t) buf + i))) == expected_tag);
|
||||
}
|
||||
}
|
||||
|
||||
void madvise_dontneed() {
|
||||
const size_t len = 100'000;
|
||||
void *ptr = mmap(NULL, len, PROT_READ | PROT_WRITE | PROT_MTE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
assert(ptr != MAP_FAILED);
|
||||
|
||||
// check that 0 is the initial tag
|
||||
check_tag(ptr, len, 0);
|
||||
|
||||
arm_mte_tag_and_clear_mem(set_pointer_tag(ptr, 1), len);
|
||||
check_tag(ptr, len, 1);
|
||||
|
||||
memset(set_pointer_tag(ptr, 1), 1, len);
|
||||
|
||||
assert(madvise(ptr, len, MADV_DONTNEED) == 0);
|
||||
// check that MADV_DONTNEED resets the tag
|
||||
check_tag(ptr, len, 0);
|
||||
|
||||
// check that MADV_DONTNEED clears the memory
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
assert(((u8 *) ptr)[i] == 0);
|
||||
}
|
||||
|
||||
// check that mistagged read after MADV_DONTNEED fails
|
||||
expect_read_segv(*((u8 *) set_pointer_tag(ptr, 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),
|
||||
TEST(madvise_dontneed),
|
||||
#undef TEST
|
||||
};
|
||||
|
||||
void segv_handler(int, siginfo_t *si, void *) {
|
||||
if (expected_segv_code == 0 || expected_segv_code != si->si_code) {
|
||||
fprintf(stderr, "received unexpected SEGV_CODE %i", si->si_code);
|
||||
exit(139); // standard exit code for SIGSEGV
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
79
androidtest/src/grapheneos/hmalloc/MemtagTest.java
Normal file
79
androidtest/src/grapheneos/hmalloc/MemtagTest.java
Normal file
|
@ -0,0 +1,79 @@
|
|||
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.util.ArrayList;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@RunWith(DeviceJUnit4ClassRunner.class)
|
||||
public class MemtagTest extends BaseHostJUnit4Test {
|
||||
private static final String TEST_BINARY = "/data/local/tmp/memtag_test";
|
||||
|
||||
private void runTest(String name) 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("stderr", "", result.getStderr());
|
||||
assertEquals("process exit code", 0, result.getExitCode().intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tag_distinctness() throws DeviceNotAvailableException {
|
||||
runTest("tag_distinctness");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void read_after_free() throws DeviceNotAvailableException {
|
||||
runTest("read_after_free");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void write_after_free() throws DeviceNotAvailableException {
|
||||
runTest("write_after_free");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void underflow_read() throws DeviceNotAvailableException {
|
||||
runTest("underflow_read");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void underflow_write() throws DeviceNotAvailableException {
|
||||
runTest("underflow_write");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void overflow_read() throws DeviceNotAvailableException {
|
||||
runTest("overflow_read");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void overflow_write() throws DeviceNotAvailableException {
|
||||
runTest("overflow_write");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void untagged_read() throws DeviceNotAvailableException {
|
||||
runTest("untagged_read");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void untagged_write() throws DeviceNotAvailableException {
|
||||
runTest("untagged_write");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void madvise_dontneed() throws DeviceNotAvailableException {
|
||||
runTest("madvise_dontneed");
|
||||
}
|
||||
}
|
91
arm_mte.h
Normal file
91
arm_mte.h
Normal file
|
@ -0,0 +1,91 @@
|
|||
#ifndef ARM_MTE_H
|
||||
#define ARM_MTE_H
|
||||
|
||||
#include <arm_acle.h>
|
||||
#include <stdint.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, uint64_t 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
|
|
@ -10,7 +10,10 @@ size_classes = [
|
|||
1280, 1536, 1792, 2048,
|
||||
2560, 3072, 3584, 4096,
|
||||
5120, 6144, 7168, 8192,
|
||||
10240, 12288, 14336, 16384
|
||||
10240, 12288, 14336, 16384,
|
||||
20480, 24576, 28672, 32768,
|
||||
40960, 49152, 57344, 65536,
|
||||
81920, 98304, 114688, 131072,
|
||||
]
|
||||
|
||||
size_class_slots = [
|
||||
|
@ -21,7 +24,10 @@ size_class_slots = [
|
|||
16, 16, 16, 16,
|
||||
8, 8, 8, 8,
|
||||
8, 8, 8, 8,
|
||||
6, 5, 4, 4
|
||||
6, 5, 4, 4,
|
||||
2, 2, 2, 2,
|
||||
1, 1, 1, 1,
|
||||
1, 1, 1, 1,
|
||||
]
|
||||
|
||||
fragmentation = [100 - 1 / 16 * 100]
|
||||
|
@ -43,7 +49,7 @@ for size, slots, fragmentation in zip(size_classes, size_class_slots, fragmentat
|
|||
used = size * slots
|
||||
real = page_align(used)
|
||||
print("| ", end='')
|
||||
print(size, str(fragmentation) + "%", slots, real, str(100 - used / real * 100) + "%", sep=" | ", end=" |\n")
|
||||
print(size, f"{fragmentation:.4}%", slots, real, str(100 - used / real * 100) + "%", sep=" | ", end=" |\n")
|
||||
|
||||
if len(argv) < 2:
|
||||
exit()
|
82
chacha.c
82
chacha.c
|
@ -4,6 +4,9 @@
|
|||
|
||||
#include "chacha.h"
|
||||
|
||||
// ChaCha8
|
||||
static const unsigned rounds = 8;
|
||||
|
||||
#define U8C(v) (v##U)
|
||||
#define U32C(v) (v##U)
|
||||
|
||||
|
@ -38,7 +41,7 @@
|
|||
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";
|
||||
static const char sigma[16] NONSTRING = "expand 32-byte k";
|
||||
|
||||
void chacha_keysetup(chacha_ctx *x, const u8 *k) {
|
||||
x->input[0] = U8TO32_LITTLE(sigma + 0);
|
||||
|
@ -63,55 +66,52 @@ void chacha_ivsetup(chacha_ctx *x, const u8 *iv) {
|
|||
}
|
||||
|
||||
void chacha_keystream_bytes(chacha_ctx *x, u8 *c, u32 bytes) {
|
||||
u32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
|
||||
u32 j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
|
||||
u8 *ctarget;
|
||||
u8 tmp[64];
|
||||
unsigned i;
|
||||
|
||||
if (!bytes) {
|
||||
return;
|
||||
}
|
||||
|
||||
j0 = x->input[0];
|
||||
j1 = x->input[1];
|
||||
j2 = x->input[2];
|
||||
j3 = x->input[3];
|
||||
j4 = x->input[4];
|
||||
j5 = x->input[5];
|
||||
j6 = x->input[6];
|
||||
j7 = x->input[7];
|
||||
j8 = x->input[8];
|
||||
j9 = x->input[9];
|
||||
j10 = x->input[10];
|
||||
j11 = x->input[11];
|
||||
j12 = x->input[12];
|
||||
j13 = x->input[13];
|
||||
j14 = x->input[14];
|
||||
j15 = x->input[15];
|
||||
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;
|
||||
}
|
||||
x0 = j0;
|
||||
x1 = j1;
|
||||
x2 = j2;
|
||||
x3 = j3;
|
||||
x4 = j4;
|
||||
x5 = j5;
|
||||
x6 = j6;
|
||||
x7 = j7;
|
||||
x8 = j8;
|
||||
x9 = j9;
|
||||
x10 = j10;
|
||||
x11 = j11;
|
||||
x12 = j12;
|
||||
x13 = j13;
|
||||
x14 = j14;
|
||||
x15 = j15;
|
||||
for (i = 8; i > 0; i -= 2) {
|
||||
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)
|
||||
|
@ -163,7 +163,7 @@ void chacha_keystream_bytes(chacha_ctx *x, u8 *c, u32 bytes) {
|
|||
|
||||
if (bytes <= 64) {
|
||||
if (bytes < 64) {
|
||||
for (i = 0; i < bytes; ++i) {
|
||||
for (unsigned i = 0; i < bytes; ++i) {
|
||||
ctarget[i] = c[i];
|
||||
}
|
||||
}
|
||||
|
|
23
config/default.mk
Normal file
23
config/default.mk
Normal file
|
@ -0,0 +1,23 @@
|
|||
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
|
23
config/light.mk
Normal file
23
config/light.mk
Normal file
|
@ -0,0 +1,23 @@
|
|||
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
|
1653
h_malloc.c
1653
h_malloc.c
File diff suppressed because it is too large
Load diff
|
@ -5,7 +5,9 @@
|
|||
|
||||
#include <malloc.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef H_MALLOC_PREFIX
|
||||
#define h_malloc malloc
|
||||
|
@ -21,6 +23,7 @@ __BEGIN_DECLS
|
|||
#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
|
||||
|
@ -30,7 +33,12 @@ __BEGIN_DECLS
|
|||
#define h_malloc_get_state malloc_get_state
|
||||
#define h_malloc_set_state malloc_set_state
|
||||
|
||||
#define h_iterate iterate
|
||||
#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
|
||||
|
||||
|
@ -40,9 +48,10 @@ __BEGIN_DECLS
|
|||
#endif
|
||||
|
||||
// C standard
|
||||
void *h_malloc(size_t size);
|
||||
void *h_calloc(size_t nmemb, size_t size);
|
||||
void *h_realloc(void *ptr, size_t size);
|
||||
__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);
|
||||
|
||||
|
@ -63,35 +72,43 @@ 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);
|
||||
void *h_valloc(size_t size);
|
||||
void *h_pvalloc(size_t size);
|
||||
void h_cfree(void *ptr);
|
||||
#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 __mallinfo_narenas(void);
|
||||
size_t __mallinfo_nbins(void);
|
||||
struct mallinfo __mallinfo_arena_info(size_t arena);
|
||||
struct mallinfo __mallinfo_bin_info(size_t arena, size_t bin);
|
||||
int h_iterate(uintptr_t base, size_t size, void (*callback)(uintptr_t ptr, size_t size, void *arg),
|
||||
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
|
||||
|
||||
// custom extensions
|
||||
// hardened_malloc extensions
|
||||
|
||||
// return an upper bound on object size for any pointer based on malloc metadata
|
||||
size_t h_malloc_object_size(void *ptr);
|
||||
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(void *ptr);
|
||||
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.
|
||||
|
@ -105,6 +122,8 @@ size_t h_malloc_object_size_fast(void *ptr);
|
|||
// passed size matches the allocated size.
|
||||
void h_free_sized(void *ptr, size_t expected_size);
|
||||
|
||||
__END_DECLS
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
101
memory.c
101
memory.c
|
@ -1,7 +1,10 @@
|
|||
#include <errno.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#ifdef LABEL_MEMORY
|
||||
#include <sys/prctl.h>
|
||||
#endif
|
||||
|
||||
#ifndef PR_SET_VMA
|
||||
#define PR_SET_VMA 0x53564d41
|
||||
|
@ -14,8 +17,8 @@
|
|||
#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);
|
||||
static void *memory_map_prot(size_t size, int prot) {
|
||||
void *p = mmap(NULL, size, prot, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
|
||||
if (unlikely(p == MAP_FAILED)) {
|
||||
if (errno != ENOMEM) {
|
||||
fatal_error("non-ENOMEM mmap failure");
|
||||
|
@ -25,30 +28,50 @@ void *memory_map(size_t size) {
|
|||
return p;
|
||||
}
|
||||
|
||||
int memory_map_fixed(void *ptr, size_t size) {
|
||||
void *p = mmap(ptr, size, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED, -1, 0);
|
||||
if (unlikely(p == MAP_FAILED)) {
|
||||
if (errno != ENOMEM) {
|
||||
fatal_error("non-ENOMEM MAP_FIXED mmap failure");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
void *memory_map(size_t size) {
|
||||
return memory_map_prot(size, PROT_NONE);
|
||||
}
|
||||
|
||||
int memory_unmap(void *ptr, size_t size) {
|
||||
int ret = munmap(ptr, size);
|
||||
#ifdef HAS_ARM_MTE
|
||||
// Note that PROT_MTE can't be cleared via mprotect
|
||||
void *memory_map_mte(size_t size) {
|
||||
return memory_map_prot(size, PROT_MTE);
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool memory_map_fixed_prot(void *ptr, size_t size, int prot) {
|
||||
void *p = mmap(ptr, size, prot, 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_map_fixed(void *ptr, size_t size) {
|
||||
return memory_map_fixed_prot(ptr, size, PROT_NONE);
|
||||
}
|
||||
|
||||
#ifdef HAS_ARM_MTE
|
||||
// Note that PROT_MTE can't be cleared via mprotect
|
||||
bool memory_map_fixed_mte(void *ptr, size_t size) {
|
||||
return memory_map_fixed_prot(ptr, size, PROT_MTE);
|
||||
}
|
||||
#endif
|
||||
|
||||
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 int memory_protect_prot(void *ptr, size_t size, int prot, UNUSED int pkey) {
|
||||
static bool memory_protect_prot(void *ptr, size_t size, int prot, UNUSED int pkey) {
|
||||
#ifdef USE_PKEY
|
||||
int ret = pkey_mprotect(ptr, size, prot, pkey);
|
||||
bool ret = pkey_mprotect(ptr, size, prot, pkey);
|
||||
#else
|
||||
int ret = mprotect(ptr, size, prot);
|
||||
bool ret = mprotect(ptr, size, prot);
|
||||
#endif
|
||||
if (unlikely(ret) && errno != ENOMEM) {
|
||||
fatal_error("non-ENOMEM mprotect failure");
|
||||
|
@ -56,42 +79,50 @@ static int memory_protect_prot(void *ptr, size_t size, int prot, UNUSED int pkey
|
|||
return ret;
|
||||
}
|
||||
|
||||
int memory_protect_ro(void *ptr, size_t size) {
|
||||
bool memory_protect_ro(void *ptr, size_t size) {
|
||||
return memory_protect_prot(ptr, size, PROT_READ, -1);
|
||||
}
|
||||
|
||||
int memory_protect_rw(void *ptr, size_t size) {
|
||||
bool memory_protect_rw(void *ptr, size_t size) {
|
||||
return memory_protect_prot(ptr, size, PROT_READ|PROT_WRITE, -1);
|
||||
}
|
||||
|
||||
int memory_protect_rw_metadata(void *ptr, size_t size) {
|
||||
bool memory_protect_rw_metadata(void *ptr, size_t size) {
|
||||
return memory_protect_prot(ptr, size, PROT_READ|PROT_WRITE, get_metadata_key());
|
||||
}
|
||||
|
||||
int memory_remap(void *old, size_t old_size, size_t new_size) {
|
||||
#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);
|
||||
if (unlikely(ptr == MAP_FAILED)) {
|
||||
if (errno != ENOMEM) {
|
||||
fatal_error("non-ENOMEM mremap failure");
|
||||
}
|
||||
return 1;
|
||||
bool ret = ptr == MAP_FAILED;
|
||||
if (unlikely(ret) && errno != ENOMEM) {
|
||||
fatal_error("non-ENOMEM mremap failure");
|
||||
}
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int memory_remap_fixed(void *old, size_t old_size, void *new, size_t new_size) {
|
||||
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);
|
||||
if (unlikely(ptr == MAP_FAILED)) {
|
||||
if (errno != ENOMEM) {
|
||||
fatal_error("non-ENOMEM MREMAP_FIXED mremap failure");
|
||||
}
|
||||
return 1;
|
||||
bool ret = ptr == MAP_FAILED;
|
||||
if (unlikely(ret) && errno != ENOMEM) {
|
||||
fatal_error("non-ENOMEM MREMAP_FIXED mremap failure");
|
||||
}
|
||||
return 0;
|
||||
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;
|
||||
}
|
||||
|
||||
void memory_set_name(UNUSED void *ptr, UNUSED size_t size, UNUSED const char *name) {
|
||||
bool memory_set_name(UNUSED void *ptr, UNUSED size_t size, UNUSED const char *name) {
|
||||
#ifdef LABEL_MEMORY
|
||||
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, ptr, size, name);
|
||||
return prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, ptr, size, name);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
|
30
memory.h
30
memory.h
|
@ -1,18 +1,32 @@
|
|||
#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);
|
||||
int memory_map_fixed(void *ptr, size_t size);
|
||||
int memory_unmap(void *ptr, size_t size);
|
||||
int memory_protect_ro(void *ptr, size_t size);
|
||||
int memory_protect_rw(void *ptr, size_t size);
|
||||
int memory_protect_rw_metadata(void *ptr, size_t size);
|
||||
int memory_remap(void *old, size_t old_size, size_t new_size);
|
||||
int memory_remap_fixed(void *old, size_t old_size, void *new, size_t new_size);
|
||||
void memory_set_name(void *ptr, size_t size, const char *name);
|
||||
#ifdef HAS_ARM_MTE
|
||||
void *memory_map_mte(size_t size);
|
||||
#endif
|
||||
bool memory_map_fixed(void *ptr, size_t size);
|
||||
#ifdef HAS_ARM_MTE
|
||||
bool memory_map_fixed_mte(void *ptr, size_t size);
|
||||
#endif
|
||||
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
|
||||
|
|
50
memtag.h
Normal file
50
memtag.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
#ifndef MEMTAG_H
|
||||
#define MEMTAG_H
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#ifdef HAS_ARM_MTE
|
||||
#include "arm_mte.h"
|
||||
#define MEMTAG 1
|
||||
// Note that bionic libc always reserves tag 0 via PR_MTE_TAG_MASK prctl
|
||||
#define RESERVED_TAG 0
|
||||
#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
|
80
new.cc
80
new.cc
|
@ -1,12 +1,12 @@
|
|||
// needed with libstdc++ but not libc++
|
||||
#if __has_include(<bits/functexcept.h>)
|
||||
#include <bits/functexcept.h>
|
||||
#endif
|
||||
|
||||
#include <new>
|
||||
|
||||
#define noreturn
|
||||
|
||||
extern "C" {
|
||||
#include "h_malloc.h"
|
||||
#include "util.h"
|
||||
}
|
||||
|
||||
COLD static void *handle_out_of_memory(size_t size, bool nothrow) {
|
||||
void *ptr = nullptr;
|
||||
|
@ -79,3 +79,75 @@ EXPORT void operator delete(void *ptr, size_t size) noexcept {
|
|||
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);
|
||||
}
|
||||
|
|
29
pages.c
29
pages.c
|
@ -4,13 +4,14 @@
|
|||
#include "pages.h"
|
||||
#include "util.h"
|
||||
|
||||
static uintptr_t alignment_ceiling(uintptr_t s, uintptr_t alignment) {
|
||||
return ((s) + (alignment - 1)) & ((~alignment) + 1);
|
||||
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) {
|
||||
void *allocate_pages(size_t usable_size, size_t guard_size, bool unprotect, const char *name) {
|
||||
size_t real_size;
|
||||
if (unlikely(__builtin_add_overflow(usable_size, guard_size * 2, &real_size))) {
|
||||
if (unlikely(add_guards(usable_size, guard_size, &real_size))) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
@ -18,6 +19,7 @@ void *allocate_pages(size_t usable_size, size_t guard_size, bool unprotect) {
|
|||
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);
|
||||
|
@ -26,12 +28,8 @@ void *allocate_pages(size_t usable_size, size_t guard_size, bool unprotect) {
|
|||
return usable;
|
||||
}
|
||||
|
||||
void deallocate_pages(void *usable, size_t usable_size, size_t guard_size) {
|
||||
memory_unmap((char *)usable - guard_size, usable_size + guard_size * 2);
|
||||
}
|
||||
|
||||
void *allocate_pages_aligned(size_t usable_size, size_t alignment, size_t guard_size) {
|
||||
usable_size = PAGE_CEILING(usable_size);
|
||||
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;
|
||||
|
@ -44,7 +42,7 @@ void *allocate_pages_aligned(size_t usable_size, size_t alignment, size_t guard_
|
|||
}
|
||||
|
||||
size_t real_alloc_size;
|
||||
if (unlikely(__builtin_add_overflow(alloc_size, guard_size * 2, &real_alloc_size))) {
|
||||
if (unlikely(add_guards(alloc_size, guard_size, &real_alloc_size))) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
@ -53,10 +51,11 @@ void *allocate_pages_aligned(size_t usable_size, size_t alignment, size_t guard_
|
|||
if (unlikely(real == NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
memory_set_name(real, real_alloc_size, name);
|
||||
|
||||
void *usable = (char *)real + guard_size;
|
||||
|
||||
size_t lead_size = alignment_ceiling((uintptr_t)usable, alignment) - (uintptr_t)usable;
|
||||
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;
|
||||
|
||||
|
@ -81,3 +80,9 @@ void *allocate_pages_aligned(size_t usable_size, size_t alignment, size_t guard_
|
|||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
21
pages.h
21
pages.h
|
@ -3,15 +3,30 @@
|
|||
|
||||
#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
|
||||
#define PAGE_CEILING(s) (((s) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))
|
||||
|
||||
void *allocate_pages(size_t usable_size, size_t guard_size, bool unprotect);
|
||||
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);
|
||||
void *allocate_pages_aligned(size_t usable_size, size_t alignment, 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
|
||||
|
|
59
random.c
59
random.c
|
@ -5,20 +5,10 @@
|
|||
#include "random.h"
|
||||
#include "util.h"
|
||||
|
||||
#if __has_include(<sys/random.h>)
|
||||
// glibc 2.25 and later
|
||||
#include <sys/random.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
static ssize_t getrandom(void *buf, size_t buflen, unsigned int flags) {
|
||||
return syscall(SYS_getrandom, buf, buflen, flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void get_random_seed(void *buf, size_t size) {
|
||||
while (size > 0) {
|
||||
while (size) {
|
||||
ssize_t r;
|
||||
|
||||
do {
|
||||
|
@ -39,19 +29,48 @@ void random_state_init(struct random_state *state) {
|
|||
get_random_seed(rnd, sizeof(rnd));
|
||||
chacha_keysetup(&state->ctx, rnd);
|
||||
chacha_ivsetup(&state->ctx, rnd + CHACHA_KEY_SIZE);
|
||||
chacha_keystream_bytes(&state->ctx, state->cache, RANDOM_CACHE_SIZE);
|
||||
state->index = 0;
|
||||
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) {
|
||||
chacha_keystream_bytes(&state->ctx, state->cache, RANDOM_CACHE_SIZE);
|
||||
state->index = 0;
|
||||
state->reseed += RANDOM_CACHE_SIZE;
|
||||
} else {
|
||||
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) {
|
||||
|
@ -73,7 +92,7 @@ u16 get_random_u16_uniform(struct random_state *state, u16 bound) {
|
|||
if (leftover < bound) {
|
||||
u16 threshold = -bound % bound;
|
||||
while (leftover < threshold) {
|
||||
random = get_random_u16(state);
|
||||
random = get_random_u16(state);
|
||||
multiresult = random * bound;
|
||||
leftover = (u16)multiresult;
|
||||
}
|
||||
|
@ -100,7 +119,7 @@ u64 get_random_u64_uniform(struct random_state *state, u64 bound) {
|
|||
if (leftover < bound) {
|
||||
u64 threshold = -bound % bound;
|
||||
while (leftover < threshold) {
|
||||
random = get_random_u64(state);
|
||||
random = get_random_u64(state);
|
||||
multiresult = random * bound;
|
||||
leftover = multiresult;
|
||||
}
|
||||
|
|
2
random.h
2
random.h
|
@ -15,6 +15,8 @@ struct random_state {
|
|||
};
|
||||
|
||||
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);
|
||||
|
|
43
test/.gitignore
vendored
43
test/.gitignore
vendored
|
@ -1 +1,44 @@
|
|||
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,16 +1,76 @@
|
|||
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
|
||||
|
||||
CPPFLAGS += \
|
||||
-DSLAB_CANARY=$(CONFIG_SLAB_CANARY)
|
||||
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
|
||||
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__
|
||||
|
|
0
test/__init__.py
Normal file
0
test/__init__.py
Normal file
|
@ -1,11 +1,12 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
struct foo {
|
||||
uint64_t a, b, c, d;
|
||||
};
|
||||
|
||||
__attribute__((optimize(0)))
|
||||
int main(void) {
|
||||
OPTNONE int main(void) {
|
||||
void *p = new char;
|
||||
struct foo *c = (struct foo *)p;
|
||||
delete c;
|
|
@ -1,8 +1,9 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
__attribute__((optimize(0)))
|
||||
int main(void) {
|
||||
void *p = malloc(128 * 1024);
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
void *p = malloc(256 * 1024);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
|
@ -1,12 +1,13 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
__attribute__((optimize(0)))
|
||||
int main(void) {
|
||||
void *p = malloc(128 * 1024);
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
void *p = malloc(256 * 1024);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
void *q = malloc(128 * 1024);
|
||||
void *q = malloc(256 * 1024);
|
||||
if (!q) {
|
||||
return 1;
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
__attribute__((optimize(0)))
|
||||
int main(void) {
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
void *p = malloc(16);
|
||||
if (!p) {
|
||||
return 1;
|
|
@ -1,7 +1,8 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
__attribute__((optimize(0)))
|
||||
int main(void) {
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
void *p = malloc(16);
|
||||
if (!p) {
|
||||
return 1;
|
8
test/impossibly_large_malloc.c
Normal file
8
test/impossibly_large_malloc.c
Normal file
|
@ -0,0 +1,8 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(-8);
|
||||
return !(p == NULL);
|
||||
}
|
|
@ -2,8 +2,9 @@
|
|||
|
||||
#include <sys/mman.h>
|
||||
|
||||
__attribute__((optimize(0)))
|
||||
int main(void) {
|
||||
#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) {
|
|
@ -1,7 +1,8 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
__attribute__((optimize(0)))
|
||||
int main(void) {
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(16);
|
||||
if (!p) {
|
||||
return 1;
|
|
@ -1,7 +1,8 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
__attribute__((optimize(0)))
|
||||
int main(void) {
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(16);
|
||||
if (!p) {
|
||||
return 1;
|
|
@ -2,8 +2,9 @@
|
|||
|
||||
#include <sys/mman.h>
|
||||
|
||||
__attribute__((optimize(0)))
|
||||
int main(void) {
|
||||
#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) {
|
15
test/invalid_malloc_object_size_small.c
Normal file
15
test/invalid_malloc_object_size_small.c
Normal file
|
@ -0,0 +1,15 @@
|
|||
#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;
|
||||
}
|
15
test/invalid_malloc_object_size_small_quarantine.c
Normal file
15
test/invalid_malloc_object_size_small_quarantine.c
Normal file
|
@ -0,0 +1,15 @@
|
|||
#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;
|
||||
}
|
13
test/invalid_malloc_usable_size_small.c
Normal file
13
test/invalid_malloc_usable_size_small.c
Normal file
|
@ -0,0 +1,13 @@
|
|||
#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;
|
||||
}
|
13
test/invalid_malloc_usable_size_small_quarantine.c
Normal file
13
test/invalid_malloc_usable_size_small_quarantine.c
Normal file
|
@ -0,0 +1,13 @@
|
|||
#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;
|
||||
}
|
18
test/large_array_growth.c
Normal file
18
test/large_array_growth.c
Normal file
|
@ -0,0 +1,18 @@
|
|||
#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;
|
||||
}
|
||||
}
|
44
test/mallinfo.c
Normal file
44
test/mallinfo.c
Normal file
|
@ -0,0 +1,44 @@
|
|||
#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();
|
||||
}
|
44
test/mallinfo2.c
Normal file
44
test/mallinfo2.c
Normal file
|
@ -0,0 +1,44 @@
|
|||
#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();
|
||||
}
|
36
test/malloc_info.c
Normal file
36
test/malloc_info.c
Normal file
|
@ -0,0 +1,36 @@
|
|||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.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
|
||||
}
|
12
test/malloc_object_size.c
Normal file
12
test/malloc_object_size.c
Normal file
|
@ -0,0 +1,12 @@
|
|||
#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);
|
||||
}
|
12
test/malloc_object_size_offset.c
Normal file
12
test/malloc_object_size_offset.c
Normal file
|
@ -0,0 +1,12 @@
|
|||
#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);
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static unsigned size_classes[] = {
|
||||
static size_t size_classes[] = {
|
||||
/* large */ 4 * 1024 * 1024,
|
||||
/* 0 */ 0,
|
||||
/* 16 */ 16, 32, 48, 64, 80, 96, 112, 128,
|
||||
|
@ -13,7 +13,12 @@ static unsigned size_classes[] = {
|
|||
/* 256 */ 1280, 1536, 1792, 2048,
|
||||
/* 512 */ 2560, 3072, 3584, 4096,
|
||||
/* 1024 */ 5120, 6144, 7168, 8192,
|
||||
/* 2048 */ 10240, 12288, 14336, 16384
|
||||
/* 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]))
|
||||
|
@ -27,9 +32,9 @@ int main(void) {
|
|||
|
||||
void *p[N_SIZE_CLASSES];
|
||||
for (unsigned i = 0; i < N_SIZE_CLASSES; i++) {
|
||||
unsigned size = size_classes[i];
|
||||
size_t size = size_classes[i];
|
||||
p[i] = malloc(size);
|
||||
if (!p) {
|
||||
if (!p[i]) {
|
||||
return 1;
|
||||
}
|
||||
void *q = malloc(size);
|
||||
|
|
15
test/overflow_large_1_byte.c
Normal file
15
test/overflow_large_1_byte.c
Normal file
|
@ -0,0 +1,15 @@
|
|||
#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;
|
||||
}
|
15
test/overflow_large_8_byte.c
Normal file
15
test/overflow_large_8_byte.c
Normal file
|
@ -0,0 +1,15 @@
|
|||
#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;
|
||||
}
|
15
test/overflow_small_1_byte.c
Normal file
15
test/overflow_small_1_byte.c
Normal file
|
@ -0,0 +1,15 @@
|
|||
#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;
|
||||
}
|
16
test/overflow_small_8_byte.c
Normal file
16
test/overflow_small_8_byte.c
Normal file
|
@ -0,0 +1,16 @@
|
|||
#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;
|
||||
}
|
21
test/read_after_free_large.c
Normal file
21
test/read_after_free_large.c
Normal file
|
@ -0,0 +1,21 @@
|
|||
#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;
|
||||
}
|
|
@ -2,8 +2,9 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
__attribute__((optimize(0)))
|
||||
int main(void) {
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(16);
|
||||
if (!p) {
|
||||
return 1;
|
||||
|
@ -12,6 +13,9 @@ int main(void) {
|
|||
free(p);
|
||||
for (size_t i = 0; i < 16; i++) {
|
||||
printf("%x\n", p[i]);
|
||||
if (p[i] != '\0') {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
__attribute__((optimize(0)))
|
||||
int main(void) {
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(0);
|
||||
if (!p) {
|
||||
return 1;
|
33
test/realloc_init.c
Normal file
33
test/realloc_init.c
Normal file
|
@ -0,0 +1,33 @@
|
|||
#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;
|
||||
}
|
25
test/simple-memory-corruption/.gitignore
vendored
25
test/simple-memory-corruption/.gitignore
vendored
|
@ -1,25 +0,0 @@
|
|||
delete_type_size_mismatch
|
||||
double_free_large
|
||||
double_free_large_delayed
|
||||
double_free_small
|
||||
double_free_small_delayed
|
||||
eight_byte_overflow_large
|
||||
eight_byte_overflow_small
|
||||
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
|
|
@ -1,31 +0,0 @@
|
|||
EXECUTABLES := \
|
||||
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_free \
|
||||
uninitialized_realloc \
|
||||
uninitialized_malloc_usable_size \
|
||||
eight_byte_overflow_small \
|
||||
eight_byte_overflow_large \
|
||||
string_overflow \
|
||||
delete_type_size_mismatch
|
||||
|
||||
all: $(EXECUTABLES)
|
||||
|
||||
clean:
|
||||
rm -f $(EXECUTABLES)
|
|
@ -1,12 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
__attribute__((optimize(0)))
|
||||
int main(void) {
|
||||
char *p = malloc(128 * 1024);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
*(p + 128 * 1024 + 7) = 0;
|
||||
free(p);
|
||||
return 0;
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
__attribute__((optimize(0)))
|
||||
int main(void) {
|
||||
char *p = malloc(8);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
*(p + 8 + 7) = 0;
|
||||
free(p);
|
||||
return 0;
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
__attribute__((optimize(0)))
|
||||
int main(void) {
|
||||
char *p = malloc(128 * 1024);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
memset(p, 'a', 16);
|
||||
free(p);
|
||||
for (size_t i = 0; i < 128 * 1024; i++) {
|
||||
printf("%x\n", p[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
__attribute__((optimize(0)))
|
||||
int main(void) {
|
||||
char *p = malloc(128 * 1024);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
free(p);
|
||||
char *q = malloc(128 * 1024);
|
||||
p[64 * 1024 + 1] = 'a';
|
||||
return 0;
|
||||
}
|
|
@ -4,8 +4,9 @@
|
|||
|
||||
#include <malloc.h>
|
||||
|
||||
__attribute__((optimize(0)))
|
||||
int main(void) {
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(16);
|
||||
if (!p) {
|
||||
return 1;
|
242
test/test_smc.py
Normal file
242
test/test_smc.py
Normal file
|
@ -0,0 +1,242 @@
|
|||
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()
|
10
test/test_util.h
Normal file
10
test/test_util.h
Normal file
|
@ -0,0 +1,10 @@
|
|||
#ifndef TEST_UTIL_H
|
||||
#define TEST_UTIL_H
|
||||
|
||||
#ifdef __clang__
|
||||
#define OPTNONE __attribute__((optnone))
|
||||
#else
|
||||
#define OPTNONE __attribute__((optimize(0)))
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -1,8 +1,9 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
__attribute__((optimize(0)))
|
||||
int main(void) {
|
||||
char *p = malloc(128 * 1024);
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(256 * 1024);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
__attribute__((optimize(0)))
|
||||
int main(void) {
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(16);
|
||||
if (!p) {
|
||||
return 1;
|
12
test/unaligned_malloc_usable_size_small.c
Normal file
12
test/unaligned_malloc_usable_size_small.c
Normal file
|
@ -0,0 +1,12 @@
|
|||
#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,7 +1,8 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
__attribute__((optimize(0)))
|
||||
int main(void) {
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
free((void *)1);
|
||||
return 0;
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
#include <malloc.h>
|
||||
|
||||
__attribute__((optimize(0)))
|
||||
int main(void) {
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
malloc_usable_size((void *)1);
|
||||
return 0;
|
||||
}
|
14
test/uninitialized_read_large.c
Normal file
14
test/uninitialized_read_large.c
Normal file
|
@ -0,0 +1,14 @@
|
|||
#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;
|
||||
}
|
14
test/uninitialized_read_small.c
Normal file
14
test/uninitialized_read_small.c
Normal file
|
@ -0,0 +1,14 @@
|
|||
#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,7 +1,8 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
__attribute__((optimize(0)))
|
||||
int main(void) {
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
void *p = realloc((void *)1, 16);
|
||||
if (!p) {
|
||||
return 1;
|
|
@ -1,9 +1,9 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
__attribute__((optimize(0)))
|
||||
int main(void) {
|
||||
char *p = malloc(128 * 1024);
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(256 * 1024);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
16
test/write_after_free_large_reuse.c
Normal file
16
test/write_after_free_large_reuse.c
Normal file
|
@ -0,0 +1,16 @@
|
|||
#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,8 +1,8 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
__attribute__((optimize(0)))
|
||||
int main(void) {
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(128);
|
||||
if (!p) {
|
||||
return 1;
|
|
@ -1,14 +1,15 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
__attribute__((optimize(0)))
|
||||
int main(void) {
|
||||
#include "test_util.h"
|
||||
#include "../util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(128);
|
||||
if (!p) {
|
||||
return 1;
|
||||
}
|
||||
free(p);
|
||||
char *q = malloc(128);
|
||||
UNUSED char *q = malloc(128);
|
||||
|
||||
p[65] = 'a';
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
__attribute__((optimize(0)))
|
||||
int main(void) {
|
||||
#include "test_util.h"
|
||||
|
||||
OPTNONE int main(void) {
|
||||
char *p = malloc(0);
|
||||
if (!p) {
|
||||
return 1;
|
3675
third_party/libdivide.h
vendored
3675
third_party/libdivide.h
vendored
File diff suppressed because it is too large
Load diff
13
util.c
13
util.c
|
@ -4,8 +4,15 @@
|
|||
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <async_safe/log.h>
|
||||
int mallopt(int param, int value);
|
||||
#define M_BIONIC_RESTORE_DEFAULT_SIGABRT_HANDLER (-1003)
|
||||
#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);
|
||||
|
@ -21,11 +28,17 @@ static int write_full(int fd, const char *buf, size_t length) {
|
|||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
COLD noreturn void fatal_error(const char *s) {
|
||||
#ifdef __ANDROID__
|
||||
mallopt(M_BIONIC_RESTORE_DEFAULT_SIGABRT_HANDLER, 0);
|
||||
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
|
||||
}
|
||||
|
|
66
util.h
66
util.h
|
@ -1,11 +1,17 @@
|
|||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdnoreturn.h>
|
||||
|
||||
// C11 noreturn doesn't work in C++
|
||||
#define noreturn __attribute__((noreturn))
|
||||
|
||||
#define likely(x) __builtin_expect(!!(x), 1)
|
||||
#define likely51(x) __builtin_expect_with_probability(!!(x), 1, 0.51)
|
||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||
#define unlikely51(x) __builtin_expect_with_probability(!!(x), 0, 0.51)
|
||||
|
||||
#define min(x, y) ({ \
|
||||
__typeof__(x) _x = (x); \
|
||||
|
@ -26,11 +32,12 @@
|
|||
#define STRINGIFY(s) #s
|
||||
#define ALIAS(f) __attribute__((alias(STRINGIFY(f))))
|
||||
|
||||
static inline int ffzl(long x) {
|
||||
return __builtin_ffsl(~x);
|
||||
}
|
||||
|
||||
COLD noreturn void fatal_error(const char *s);
|
||||
// supported since GCC 15
|
||||
#if __has_attribute (nonstring)
|
||||
# define NONSTRING __attribute__ ((nonstring))
|
||||
#else
|
||||
# define NONSTRING
|
||||
#endif
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
|
@ -38,12 +45,53 @@ typedef uint32_t u32;
|
|||
typedef uint64_t u64;
|
||||
typedef unsigned __int128 u128;
|
||||
|
||||
#ifdef CONFIG_SEAL_METADATA
|
||||
#if defined(__GLIBC__) && __GLIBC_PREREQ(2, 27)
|
||||
#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
|
||||
|
||||
#endif // CONFIG_SEAL_METADATA
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Add table
Reference in a new issue