Everything about Memory - Part 2 (Memory Tagging)



Previously, we talked about the basics of MT (Memory-Tagging) and tools that will help to make better/optimized code. I will end the Memory Tagging topic in this post so that other part's will focus on more general topics.


Let's get started!

Memory tagging hardware will not magically make C++ safer - it still requires cooperation between the compiler and the run-time.

Detecting heap-related memory bugs requires changes in malloc and free:

Malloc:

● Align the allocations by TG.

● Choose a tag T for every allocation.

● Tag the memory for the just-allocated chunk with T.

● Return the address tagged with T.

Free:

● Optionally retag the free-d memory with another tag. Strategies for choosing the tag during malloc may vary. One such strategy is to use pseudo random tags. Another strategy would ensure that no two adjacent heap chunks have the same tag (for 100% reliable linear buffer overflow/underflow detection).

Detecting stack-related memory bugs requires changes in the compiler: the function prologue will need to align all local variables by TG, tag their locations with random tags, and the tagged pointers will need to be stored in separate (virtual) registers. Optionally, the function epilogue will retag the memory. Variations of this strategy are possible, e.g. to reduce register pressure or to enable stack-use-after-scope detection.

Detecting buffer overflows in global variables also requires compiler support.


Overhead

RAM

Major sources of RAM overhead:

● Over-aligning heap objects from the natural alignment (usually, 8 or 16) to TG

● Over-aligning stack objects

● Memory tag storage: TS extra bits per every TG bytes


CPU

Major sources of CPU overhead :

● Tagging the heap/stack objects during allocation/deallocation.

● Increased working set size due to extra RAM consumption.

● Extra instructions to tag stack variables.

● Checking the tags during loads and stores - only for compiler-based instrumentation!


Security Hardening

Every Chrome release contains fixes for memory safety bugs reported to Google by external security researchers. Many of these bugs are found by sophisticated fuzzing or code inspection; we don’t know how many of them happen during normal usage of Chrome as opposed to only being triggered by clever attacks.

In other words, no amount of testing, including testing in production, will eliminate all bugs. Our hypothesis is that always-on memory tagging in production will serve as a significant obstacle for attackers as it will increase the cost of attacks, and more importantly, reduce their reliability and stealthiness.

Below we outline some strengths and weaknesses of the general MT scheme as an exploit mitigation.

Strengths

● MT prevents the root cause of many classes of attacks (as opposed to other mitigations, e.g. CFI, that prevent the consequences of memory corruption).

● Attackers sensitive to having their exploits detected and reported will be forced to use only the subset of vulnerabilities/exploit techniques unaffected by MT to evade the mitigation.

● MT can provide bug reports to the vendor with enough information to allow timely fixes (unlike most other mitigations).

● Leaking one pointer’s tag does not compromise any other tags (unlike with ASLR where leaking one pointer allows the attacker to determine the layout of an entire section of memory).

● The memory tags are hard or impossible to leak (depends on the actual hardware implementation and whether it’s protected from side channel attacks similar to Meltdown ).


Weaknesses

● Probabilistic nature of bug detection. Given a sufficient number of attempts MT can be bypassed (but if an exploit requires a chain of bugs, those low probabilities will multiply for every bug detected by MT).

● The address tag is stored in the upper pointer bits, potentially allowing the attacker to change the address tag via an integer overflow on the address. Protecting from this attack will require more compiler instrumentation.

● The address tag is stored in a fixed position in the high bits of the pointer. A buffer overflow not prevented by MT (e.g. an intra-object overflow) on little-endian processors could change the low bits of a pointer without changing the tag so that it continues to point into the same allocation (i.e. with the same tag) but increase the attacker’s access in some way.

● The address tag may be leaked and/or modified by some remaining unprotected memory corruption classes, such as intra-object-buffer-overflow or type confusion. But if the attacker has those primitives, in many cases they won’t need to bypass MT at all.

● Since the accesses to the memory and their tags are not atomic with respect to each other, racy use-after-frees may fail to be detected (but observing a racy use-after-free is probabilistic anyway).

● If the attacker can leak pointers (and hence their tags) for arbitrary objects they may be able to repeatedly cause the allocation/deallocation of new heap/stack objects until they’ve found two with matching tags to use in a use-after-free, linear overflow, etc.


The last words:

Memory tagging will not eliminate all memory safety bugs; however, our analysis indicates that memory tagging, when widely supported by hardware, will help significantly reduce the number of such bugs and is likely to complicate exploitation of the few remaining ones.


Good Luck πŸ‘



Comments