xref: /aosp_15_r20/bionic/docs/mte.md (revision 8d67ca893c1523eb926b9080dbe4e2ffd2a27ba1)
1# Arm Memory Tagging Extension (MTE) implementation
2
3AOSP supports Arm MTE to detect invalid memory accesses. The implementation is
4spread across multiple components, both within and out of the AOSP tree. This
5document gives an overview and pointers about how the various MTE features are
6implemented.
7
8For documentation of the behavior rather than the implementation, see the
9[SAC page on MTE] instead. For MTE for apps, see the [NDK page on MTE].
10
11The relevant components are:
12
13* [LLVM Project] (out of AOSP tree)
14    * Stack tagging instrumentation pass
15    * Scudo memory allocator
16* bionic
17    * libc
18    * dynamic loader
19* Zygote
20* debuggerd
21* [NDK]
22
23## MTE enablement
24
25The way MTE is requested and enabled differs between native binaries and Java
26apps. This is necessarily so, because Java apps get forked from the Zygote,
27while native executables get inintialized by the linker.
28
29### Native binaries
30
31Both AOSP and the NDK allow you to compile C/C++ code that use MTE to detect
32memory safety issues. The [NDK legacy cmake toolchain] and the
33[NDK new cmake toolchain] both support "memtag" as an argument for
34`ANDROID_SANITIZE`. NDK make has no specific support for MTE, but the
35relevant flags can be passed directly as `CFLAGS` and `LDFLAGS`.
36
37For the OS itself, [Soong] supports "memtag_[heap|stack|globals]" as
38`SANITIZE_TARGET  and as `sanitize:` attribute in Android.bp files;
39[Android make] supports the same environment variables as Soong. This passes
40the appropriate flags to the clang driver for both compile and link steps.
41
42#### Linker
43
44* For **dynamic executables** LLD has support to
45  [add appropriate dynamic sections] as defined in the [ELF standard]
46* For **static executables** and as a fallback for older devices, LLD
47  also supports [adding the Android-specific ELF note]
48
49Both of the above are controlled by the linker flag `--android-memtag-mode`
50which is [passed in by the clang driver] if
51`-fsanitize=memtag-[stack|heap|globals]` is [passed in].
52`-fsanitize=memtag` [enables all three] (even for API levels that don't
53implement the runtime for globals, which means builds from old versions
54of clang may no work with newer platform versions that support globals).
55`-fsanitize-memtag-mode` allows to choose between ASYNC and SYNC.
56
57This information can be queried using `llvm-readelf --memtag`.
58
59This information is [picked up by libc init] to decide whether to enable MTE.
60`-fsanitize-heap` controls both whether scudo tags allocations, and whether
61tag checking is enabled.
62
63#### Runtime environment (dynamic loader, libc)
64
65There are two different initialization sequences for libc, both of which end up
66calling `__libc_init_mte`.
67
68N.B. the linker has its own copy of libc, which is used when executing these
69functions. That is why we have to use `__libc_shared_globals` to communicate
70with the libc of the process we are starting.
71
72* **static executables** `__libc_init` is called from `crtbegin.c`, which calls
73                         `__libc_init_mte`
74* **dynamic executables** the linker calls `__libc_init_mte`
75
76`__libc_init_mte` figures out the appropriate MTE level that is requested by
77the process, calls `prctl` to request this from the kernel, and stores data in
78`__libc_shared_globals` which gets picked up later to enable MTE in scudo.
79
80It also does work related to stack tagging and permissive mode, which will be
81detailed later.
82
83### Apps
84
85Apps can request MTE be enabled for their process via the manifest attribute
86`android:memtagMode`. This gets interpreted by Zygote, which always runs with
87`ASYNC` MTE enabled, because MTE for a process can only be disabled after
88it has been initialized (see [Native binaries](#native-binaries)), not enabled.
89
90[decideTaggingLevel] in the Zygote figures out whether to enable MTE for
91an app, and stores it in the `runtimeFlags`, which get picked up by
92[SpecializeCommon] after forking from the Zygote.
93
94## MTE implementation
95
96### Heap Tagging
97
98Heap tagging is implemented in the scudo allocator. On `malloc` and `free`,
99scudo will update the memory's tags to prevent use-after-free and buffer
100overflows.
101
102[scudo's memtag.h] contains helper functions to deal with MTE tag management,
103which are used in [combined.h] and [secondary.h].
104
105
106### Stack Tagging
107
108Stack tagging requires instrumenting function bodies. It is implemented as
109an instrumentation pass in LLVM called [AArch64StackTagging], which sets
110the tags according to the lifetime of stack objects.
111
112The instrumentation pass also supports recording stack history, consisting of:
113
114* PC
115* Frame pointer
116* Base tag
117
118This can be used to reconstruct which stack object was referred to in an
119invalid access. The logic to reconstruct this can be found in the
120[stack script].
121
122
123Stack tagging is enabled in one of two circumstances:
124* at process startup, if the main binary or any of its dependencies are
125  compiled with `memtag-stack`
126* library compiled with `memtag-stack` is `dlopen`ed later, either directly or
127  as a dependency of a `dlopen`ed library. In this case, the
128  [__pthread_internal_remap_stack_with_mte] function is used (called from
129  `memtag_stack_dlopen_callback`). Because `dlopen`
130  is handled by the linker, we have to [store a function pointer] to the
131  process's version of the function in `__libc_shared_globals`.
132
133Enabling stack MTE consists of two operations:
134* Remapping the stacks as `PROT_MTE`
135* Allocating a stack history buffer.
136
137The first operation is only necessary when the process is running with MTE
138enabled. The second operation is also necessary when the process is not running
139with MTE enabled, because the writes to the stack history buffer are
140unconditional.
141
142libc keeps track of this through two globals:
143
144* `__libc_memtag_stack`:  whether stack MTE is enabled on the process, i.e.
145  whether the stack pages are mapped with PROT\_MTE. This is always false if
146  MTE is disabled for the process (i.e. `libc_globals.memtag` is false).
147* `__libc_memtag_stack_abi`: whether the process contains any code that was
148  compiled with memtag-stack. This is true even if the process does not have
149  MTE enabled.
150
151### Globals Tagging
152
153TODO(fmayer): write once submitted
154
155### Crash reporting
156
157For MTE crashes, debuggerd serializes special information into the Tombstone
158proto:
159
160* Tags around fault address
161* Scudo allocation history
162
163This is done in [tombstone\_proto.cpp]. The information is converted to a text
164proto in [tombstone\_proto\_to\_text.cpp].
165
166## Bootloader control
167
168The bootloader API allows userspace to enable MTE on devices that do not ship
169with MTE enabled by default.
170
171See [SAC MTE bootloader support] for the API definition. In AOSP, this API is
172implemented in [system/extras/mtectrl]. mtectrl.rc handles the property
173changes and invokes mtectrl to update the misc partition to communicate
174with the bootloader.
175
176There is also an [API in Device Policy Manager] that allows the device admin
177to enable or disable MTE under certain circumstances.
178
179The device can opt in or out of these APIs by a set of system properties:
180
181* `ro.arm64.memtag.bootctl_supported`: the system property API is supported,
182  and an option is displayed in Developer Options.
183* `ro.arm64.memtag.bootctl_settings_toggle`: an option is displayed in the
184  normal settings. This requires `ro.arm64.memtag.bootctl_supported` to be
185  true. This implies `ro.arm64.memtag.bootctl_device_policy_manager`, if it
186  is not explicitely set.
187* `ro.arm64.memtag.bootctl_device_policy_manager`: the Device Policy Manager
188  API is supported.
189
190## Permissive MTE
191
192Permissive MTE refers to a mode which, instead of crashing the process on an
193MTE fault, records a tombstone but then continues execution of the process.
194An important caveat is that system calls with invalid pointers (where the
195pointer tag does not match the memory tag) still return an error code.
196
197This mode is only available for system services, not apps. It is implemented
198in the [debugger\_signal\_handler] by disabling MTE for the faulting thread.
199Optionally, the user can ask for MTE to be re-enabled after some time.
200This is achieved by arming a timer that calls [enable_mte_signal_handler]
201upon expiry.
202
203## MTE Mode Upgrade
204
205When a system service [crashes in ASYNC mode], we set an impossible signal
206as an exit code (because that signal is always gracefully handled by libc),
207and [in init] we set `BIONIC_MEMTAG_UPGRADE_SECS`, which gets handled by
208[libc startup].
209
210[SpecializeCommon]: https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/jni/com_android_internal_os_Zygote.cpp?q=f:frameworks%2Fbase%2Fcore%2Fjni%2Fcom_android_internal_os_Zygote.cpp%20%22%20mallopt(M_BIONIC_SET_HEAP_TAGGING_LEVEL,%22&ss=android%2Fplatform%2Fsuperproject%2Fmain
211[LLVM Project]: https://github.com/llvm/llvm-project/
212[NDK]: https://android.googlesource.com/platform/ndk/
213[NDK legacy cmake toolchain]: https://android.googlesource.com/platform/ndk/+/refs/heads/main/build/cmake/android-legacy.toolchain.cmake#490
214[NDK new cmake toolchain]: https://android.googlesource.com/platform/ndk/+/refs/heads/main/build/cmake/flags.cmake#56
215[Soong]: https://cs.android.com/android/platform/superproject/main/+/main:build/soong/cc/sanitize.go?q=sanitize.go&ss=android%2Fplatform%2Fsuperproject%2Fmain
216[decideTaggingLevel]: https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/com/android/internal/os/Zygote.java?q=symbol:decideTaggingLevel
217[picked up by libc init]: https://cs.android.com/android/platform/superproject/main/+/main:bionic/libc/bionic/libc_init_mte.cpp?q=symbol:__get_tagging_level%20f:bionic
218[enables all three]: https://github.com/llvm/llvm-project/blob/e732d1ce86783b1d7fe30645fcb30434109505b9/clang/include/clang/Basic/Sanitizers.def#L62
219[passed in]: https://github.com/llvm/llvm-project/blob/ff2e619dfcd77328812a42d2ba2b11c3ff96f410/clang/lib/Driver/SanitizerArgs.cpp#L719
220[passed in by the clang driver]: https://github.com/llvm/llvm-project/blob/ff2e619dfcd77328812a42d2ba2b11c3ff96f410/clang/lib/Driver/ToolChains/CommonArgs.cpp#L1595
221[adding the Android-specific ELF note]: https://github.com/llvm/llvm-project/blob/435cb0dc5eca08cdd8d9ed0d887fa1693cc2bf33/lld/ELF/Driver.cpp#L1258
222[ELF standard]: https://github.com/ARM-software/abi-aa/blob/main/memtagabielf64/memtagabielf64.rst#6dynamic-section
223[add appropriate dynamic sections]: https://github.com/llvm/llvm-project/blob/7022498ac2f236e411e8a0f9a48669e754000a4b/lld/ELF/SyntheticSections.cpp#L1473
224[storeTags]: https://cs.android.com/android/platform/superproject/main/+/main:external/scudo/standalone/memtag.h?q=f:scudo%20f:memtag.h%20function:storeTags
225[SAC page on MTE]: https://source.android.com/docs/security/test/memory-safety/arm-mte
226[NDK page on MTE]: https://developer.android.com/ndk/guides/arm-mte
227[AArch64StackTagging]: https://github.com/llvm/llvm-project/blob/main/llvm/lib/Target/AArch64/AArch64StackTagging.cpp
228[scudo's memtag.h]: https://github.com/llvm/llvm-project/blob/main/compiler-rt/lib/scudo/standalone/memtag.h
229[combined.h]: https://github.com/llvm/llvm-project/blob/main/compiler-rt/lib/scudo/standalone/combined.h
230[secondary.h]: https://github.com/llvm/llvm-project/blob/main/compiler-rt/lib/scudo/standalone/secondary.h
231[__pthread_internal_remap_stack_with_mte]: https://cs.android.com/android/platform/superproject/main/+/main:bionic/libc/bionic/pthread_internal.cpp?q=__pthread_internal_remap_stack_with_mte
232[stack script]: https://cs.android.com/android/platform/superproject/main/+/main:development/scripts/stack?q=stack
233[Android make]: https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/config_sanitizers.mk
234[store a function pointer]: https://cs.android.com/android/platform/superproject/main/+/main:bionic/libc/bionic/libc_init_dynamic.cpp;l=168?q=memtag_stack_dlopen_callback
235[tombstone\_proto.cpp]: https://cs.android.com/android/platform/superproject/main/+/main:system/core/debuggerd/libdebuggerd/tombstone_proto.cpp?q=tombstone_proto.cpp
236[tombstone\_proto\_to\_text.cpp]: https://cs.android.com/android/platform/superproject/main/+/main:system/core/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
237[SAC MTE bootloader support]: https://source.android.com/docs/security/test/memory-safety/bootloader-support
238[system/extras/mtectrl]: https://cs.android.com/android/platform/superproject/main/+/main:system/extras/mtectrl/
239[API in Device Policy Manager]: https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/app/admin/DevicePolicyManager.java?q=symbol:setMtePolicy%20f:DevicePolicyManager.java
240[debuggerd\_signal_handler]: https://cs.android.com/android/platform/superproject/main/+/main:system/core/debuggerd/handler/debuggerd_handler.cpp?q=f:debuggerd_handler.cpp%20symbol:debuggerd_signal_handler
241[enable_mte_signal_handler]: https://cs.android.com/android/platform/superproject/main/+/main:bionic/libc/bionic/libc_init_mte.cpp?q=symbol:__enable_mte_signal_handler
242[in init]: https://cs.android.com/android/platform/superproject/main/+/main:system/core/init/service.cpp?q=f:system%2Fcore%2Finit%2Fservice.cpp%20should_upgrade_mte
243[crashes in ASYNC mode]: https://cs.android.com/android/platform/superproject/main/+/main:system/core/debuggerd/handler/debuggerd_handler.cpp;l=799?q=BIONIC_SIGNAL_ART_PROFILER
244[libc startup]: https://cs.android.com/android/platform/superproject/main/+/main:bionic/libc/bionic/libc_init_mte.cpp?q=BIONIC_MEMTAG_UPGRADE_SECS
245