update memory tagging documentation
parent
365ee6900d
commit
abe54dba27
100
README.md
100
README.md
|
@ -724,80 +724,38 @@ freeing as there would be if the kernel supported these features directly.
|
||||||
|
|
||||||
## Memory tagging
|
## Memory tagging
|
||||||
|
|
||||||
**Memory tagging has been implemented and this section is currently
|
Random tags are set for all slab allocations when allocated. 5 possible values
|
||||||
out-of-date.**
|
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
|
Slab allocations are done in a statically reserved region for each size class
|
||||||
section will be expanded to cover the details on the chosen design. The approach
|
and all metadata is in a statically reserved region, so interactions between
|
||||||
for slab allocations is currently covered, but it can also be used for the
|
different uses of the same address space is not applicable.
|
||||||
allocator metadata region and large allocations.
|
|
||||||
|
|
||||||
Memory allocations are already always multiples of naturally aligned 16 byte
|
Large allocations beyond the largest slab allocation size class (128k by
|
||||||
units, so memory tags are a natural fit into a malloc implementation due to the
|
default) are guaranteed to have randomly sized guard regions to the left and
|
||||||
16 byte alignment requirement. The only extra memory consumption will come from
|
right. Random and FIFO address space quarantines provide use-after-free
|
||||||
the hardware supported storage for the tag values (4 bits per 16 bytes).
|
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
|
When memory tagging is enabled, checking for write-after-free at allocation
|
||||||
slot on first use. The highest value will be reserved for marking freed memory
|
time and checking canaries are both disabled. Canaries will be more thoroughly
|
||||||
allocations to detect any accesses to freed memory so it won't be part of the
|
disabled when using memory tagging in the future, but Android currently has
|
||||||
generated range. Adjacent slots will be guaranteed to have distinct memory tags
|
very dynamic memory tagging support where it can be enabled/disabled at any
|
||||||
in order to guarantee that linear overflows are detected. There are a few ways
|
time which creates a barrier to optimizing by disabling redundant features.
|
||||||
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 |
|
|
||||||
|
|
||||||
## API extensions
|
## API extensions
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue