diff --git a/README.md b/README.md index 9029924..6fb3c82 100644 --- a/README.md +++ b/README.md @@ -724,80 +724,38 @@ freeing as there would be if the kernel supported these features directly. ## Memory tagging -**Memory tagging has been implemented and this section is currently -out-of-date.** +Random tags are set for all slab allocations when allocated. 5 possible values +are excluded: the default 0 tag, a statically reserved free tag, the previous +tag used for the slot, the current (or previous) tag used for the slot to the +left and the current (or previous) tag used for the slot to the right. 3 of +these are dynamic random values. When a slab allocation is freed, the reserved +free tag is set for the slot. Linear overflows are deterministically detected. +Use-after-free has deterministic detection until the freed slot goes through +both the random and FIFO quarantines, gets allocated again, goes through both +quarantines again and then finally gets allocated again for a 2nd time. Since +the default 0 tag isn't used, untagged memory can't access malloc allocations +and vice versa, although it may make sense to reuse the default tag for free +data to avoid reducing the possible random tags from 15 to 14, since freed +data is always zeroed anyway. -Integrating extensive support for ARMv8.5 memory tagging is planned and this -section will be expanded to cover the details on the chosen design. The approach -for slab allocations is currently covered, but it can also be used for the -allocator metadata region and large allocations. +Slab allocations are done in a statically reserved region for each size class +and all metadata is in a statically reserved region, so interactions between +different uses of the same address space is not applicable. -Memory allocations are already always multiples of naturally aligned 16 byte -units, so memory tags are a natural fit into a malloc implementation due to the -16 byte alignment requirement. The only extra memory consumption will come from -the hardware supported storage for the tag values (4 bits per 16 bytes). +Large allocations beyond the largest slab allocation size class (128k by +default) are guaranteed to have randomly sized guard regions to the left and +right. Random and FIFO address space quarantines provide use-after-free +detection. Random tags would still be useful for probabilistic detection of +overflows, probabilistic detection of use-after-free once the address space is +out of the quarantine and reused for another allocation and deterministic +detection of use-after-free for reuse by another allocator. We need to test +whether the cost is acceptable for enabling this by default. -The baseline policy will be to generate random tags for each slab allocation -slot on first use. The highest value will be reserved for marking freed memory -allocations to detect any accesses to freed memory so it won't be part of the -generated range. Adjacent slots will be guaranteed to have distinct memory tags -in order to guarantee that linear overflows are detected. There are a few ways -of implementing this and it will end up depending on the performance costs of -different approaches. If there's an efficient way to fetch the adjacent tag -values without wasting extra memory, it will be possible to check for them and -skip them either by generating a new random value in a loop or incrementing -past them since the tiny bit of bias wouldn't matter. Another approach would be -alternating odd and even tag values but that would substantially reduce the -overall randomness of the tags and there's very little entropy from the start. - -Once a slab allocation has been freed, the tag will be set to the reserved -value for free memory and the previous tag value will be stored inside the -allocation itself. The next time the slot is allocated, the chosen tag value -will be the previous value incremented by one to provide use-after-free -detection between generations of allocations. The stored tag will be wiped -before retagging the memory, to avoid leaking it and as part of preserving the -security property of newly allocated memory being zeroed due to zero-on-free. -It will eventually wrap all the way around, but this ends up providing a strong -guarantee for many allocation cycles due to the combination of 4 bit tags with -the FIFO quarantine feature providing delayed free. It also benefits from -random slot allocation and the randomized portion of delayed free, which result -in a further delay along with preventing a deterministic bypass by forcing a -reuse after a certain number of allocation cycles. Similarly to the initial tag -generation, tag values for adjacent allocations will be skipped by incrementing -past them. - -For example, consider this slab of allocations that are not yet used with 15 -representing the tag for free memory. For the sake of simplicity, there will be -no quarantine or other slabs for this example: - - | 15 | 15 | 15 | 15 | 15 | 15 | - -Three slots are randomly chosen for allocations, with random tags assigned (2, -7, 14) since these slots haven't ever been used and don't have saved values: - - | 15 | 2 | 15 | 7 | 14 | 15 | - -The 2nd allocation slot is freed, and is set back to the tag for free memory -(15), but with the previous tag value stored in the freed space: - - | 15 | 15 | 15 | 7 | 14 | 15 | - -The first slot is allocated for the first time, receiving the random value 3: - - | 3 | 15 | 15 | 7 | 14 | 15 | - -The 2nd slot is randomly chosen again, so the previous tag (2) is retrieved and -incremented to 3 as part of the use-after-free mitigation. An adjacent -allocation already uses the tag 3, so the tag is further incremented to 4 (it -would be incremented to 5 if one of the adjacent tags was 4): - - | 3 | 4 | 15 | 7 | 14 | 15 | - -The last slot is randomly chosen for the next allocation, and is assigned the -random value 14. However, it's placed next to an allocation with the tag 14 so -the tag is incremented and wraps around to 0: - - | 3 | 4 | 15 | 7 | 14 | 0 | +When memory tagging is enabled, checking for write-after-free at allocation +time and checking canaries are both disabled. Canaries will be more thoroughly +disabled when using memory tagging in the future, but Android currently has +very dynamic memory tagging support where it can be enabled/disabled at any +time which creates a barrier to optimizing by disabling redundant features. ## API extensions