Oddbean new post about | logout
 We've added documentation for the hardware memory tagging implementation in hardened_malloc:

https://github.com/GrapheneOS/hardened_malloc?tab=readme-ov-file#memory-tagging

GrapheneOS on Pixel 8 / Pixel 8 Pro is the first platform using ARM MTE in production. Stock Pixel OS has it as a hidden development option requiring using ADB.

GrapheneOS uses hardened_malloc as the system allocator and enables memory tagging by default. MTE is enabled for all base OS apps and nearly all executables. It's only temporarily disabled for surfaceflinger (due to upstream bug in Android 14 QPR1) and a few vendor executables.

For user installed apps, we enable MTE by default for apps without bundled native libraries and apps marked as compatible. We give users the option to enable MTE for all user installed apps in Settings > Security and users can then toggle it off for specific incompatible apps.

We added a user-facing notification for crashes caused by MTE detecting memory corruption. It makes it easy for users to copy the traceback for reporting the bug to app developers. This also means users don't need to guess when the toggle to disable MTE for an app is relevant.

Our Vanadium browser is also the first browser using MTE in production. In Vanadium, we enable Chromium's PartitionAlloc MTE implementation. PartitionAlloc's implementation isn't nearly as good as hardened_malloc, but we intend to improve PartitionAlloc's security in the future.

Chromium marks itself as compatible with MTE but then disables it as runtime, so other Chromium-based browser have MTE disabled even when the OS has it enabled. We found a bug in Chromium's MTE integration which we had to fix to avoid WebView crashes. It works smoothly for us.

We're also planning on enabling Clang's stack allocation MTE support but it currently breaks Chromium's C++ garbage collection integration along with apps doing in-process unwinding via libunwind. We want MTE for the Linux kernel too, but it integrates it as a debugging feature.

hardened_malloc's MTE implementation is already best in class, but there are some improvements to consider. It currently statically reserves a value for free slots, which reduces the random choices from 15 to 14. It may make sense to use the default 0 tag for free data instead.

MTE obsoletes hardened_malloc's canary and write-after-free check features, so we disable them when it's enabled. However, we haven't figured out an approach to save the memory reserved for canaries yet due to Android supporting dynamically toggling MTE at runtime which is messy.

hardened_malloc uses MTE for all slab allocations, which are all the allocation size classes from 16 bytes through 128k bytes with statically reserved regions for each one. It doesn't need MTE for any metadata since all metadata is in a statically reserved region solely for that.

For allocations beyond the max slab allocation size (128k), there are randomly sized guards placed before/after each allocation along with an address space quarantine on free. MTE would still be valuable for large/arbitrary overflows and use-after-free beyond the quarantine.

We need to investigate the cost of tagging the large allocations above 128k by default.

For non-MTE-capable hardware, we could consider reserving a huge region for allocations above 128k with our own best-fit implementation in userspace to separate them from non-malloc mappings.