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