MiraclePtr: how Chrome blocks use-after-free in C++
TL;DR
Half of Chrome's exploitable bugs are use-after-free. raw_ptr
In Chrome, half of the known exploitable bugs are use-after-free (UAF): code keeps a pointer to memory that has already been freed, and an attacker refills that memory with their own data. Chrome has tens of millions of lines of C++ that cannot be rewritten in a few years. The Chrome security team took another route: replace raw pointers in class fields with a lightweight smart pointer, raw_ptr<T>, which implements the BackupRefPtr algorithm from the MiraclePtr project. The goal: make roughly 50% of UAF bugs in the browser process non-exploitable.
Mechanism: a hidden reference count and quarantine
- Hidden reference count: the PartitionAlloc allocator reserves extra space in each allocation for a counter. Whenever a
raw_ptr<T>is constructed, destroyed or assigned, the counter goes up or down. - Quarantine: if memory is freed while the count is still above zero (a dangling pointer exists), PartitionAlloc does not hand it back for reuse; it holds on to it.
- Poisoning: quarantined memory is overwritten with the pattern
0xEF..EF, so reads through a dangling pointer tend to crash, which helps find and fix the bug. - Real release: only when the count reaches zero is the memory reused.
The clever part: exploiting a UAF requires the attacker to reclaim the freed memory. When that memory is held back and poisoned, the crucial step fails.
class Widget {
raw_ptr<Delegate> delegate_; // instead of Delegate* delegate_;
};
Cost: real numbers
| Item | Result (Chrome 99–102) |
|---|---|
| Browser process memory | +4.5–6.5% on Windows, +3.5–5% on Android |
| Quarantined memory | About 0.01% of browser process memory |
| Windows performance | No statistically significant regression on most top-level metrics; main-thread contention up about 7% |
| Android performance | Main-thread contention +6%, First Input Delay +1%, First Contentful Paint +0.5% |
| Pointer size | Same as a raw pointer; dereferencing costs nothing extra |
Most of the memory cost comes from the 4-byte counter per allocation, which sometimes pushes an allocation into a larger bucket. The Chrome team offset it with earlier PartitionAlloc work that had saved 10–25% of memory.
Rollout at Chrome scale
- More than 15,000 raw pointers were rewritten to
raw_ptr<T>. - Enabled for the browser process on Windows and Android from Chrome 102; current documentation says it is on by default in non-renderer processes on Android, Windows, ChromeOS, macOS, Linux and Fuchsia.
- Style rule: class/struct fields use
raw_ptr<T>instead ofT*(except renderer-only code), enforced automatically by Chromium's Clang plugin. Locals, parameters and return values stay raw.
Risks and limits
- Not everything is covered: only class/struct fields are protected; local pointers, pointers in unions and memory outside PartitionAlloc (stack, V8 heap…) are not.
- Still undefined behaviour: reading a dangling pointer is still a bug; MiraclePtr only makes it harder to exploit.
- Quarantine leaks: per the Chromium docs, memory held back by dangling pointers has caused significant leaks a couple of times.
- Bug bounty: at launch, Chrome said MiraclePtr would not factor into bug severity until it reached all users.
How to apply it
- Dev: If your C++ project has many long-lived objects referencing each other, that is where UAF lives. Audit non-owning pointer fields and consider smart pointers or an allocator with quarantine.
- Tech Lead: Copy Chrome's playbook: a clear rule (class fields use a safe type), automated enforcement, an exemption process with recorded reasons, and A/B performance testing before a broad rollout.
- PM: Paying around 5% memory to remove exploitability from half of the most dangerous bug class is a good trade. Decide with measurements like Chrome did, not gut feeling.
Sources & references
- Use-after-freedom: MiraclePtr — Google Security Blog
- raw_ptr<T> (aka MiraclePtr, aka BackupRefPtr) — Chromium