This article by Nicolas Stefanski at Synacktiv provides a high quality technical overview of our hardened_malloc project used in GrapheneOS:

https://www.synacktiv.com/en/publications/exploring-grapheneos-secure-allocator-hardened-malloc

It has great coverage of the memory layout, memory tagging integration, slab quarantines and allocation approach.

The only major omission is that it doesn’t cover write-after-free check performed at allocation time for slab allocations when MTE is unavailable. MTE can be dynamically enabled which is why canaries aren’t yet fully gone with it yet, but we do plan to fully drop them with MTE.

Since hardened_malloc has no inline metadata, our canary feature exists to avoid slab allocations being placed right next to each other. Their main purpose is providing 8 bytes of padding to absorb small overflows with a leading zero byte to stop most C string related overflows.

Scudo has inline metadata with 16-bit checksums of the metadata, address and a global secret. Those exist to provide probabilistic protection of inline metadata. Our canaries only use a random value to detect linear overflow at free, which is often way too late. MTE works better.

It would be possible to use a keyed hash for canaries, but a weak hash would still be possible to bypass via leaks and SipHash is too slow. It’s only relevant to a linear overflow where catching it at free time is good enough. Our MTE support always catches it when it happens.

Building an executable with heap MTE support simply marks it as compatible and it can still be used without it. We force enable heap MTE for vendor code and third party apps. Compiler support is only really needed to use stack allocation MTE which we’re experimenting with using.

Tagging memory with MTE faults in all of the pages which is why hardened_malloc doesn’t use it for large allocations (above 128k) yet. An application can map a 1TiB allocation and we don’t want to fault in all of the pages by tagging it. We plan to enable it for the OS though.