• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# raw_ptr<T> (aka. MiraclePtr, aka. BackupRefPtr, aka. BRP)
2
3Before telling you what `raw_ptr<T>` is, we'd like you to follow one simple
4rule: think of it as a raw C++ pointer. In particular:
5- Initialize it yourself, don't assume the constructor default-initializes it
6  (it may or may not). (Always use the `raw_ptr<T> member_ = nullptr;` form of
7  initialization rather than the so-called uniform initialization form
8  (empty braces) `raw_ptr<T> member_{};` whose meaning varies with the
9  implementation.)
10- Don't assume that moving clears the pointer (it may or may not).
11- The owner of the memory must free it when the time is right, don't assume
12  `raw_ptr<T>` will free it for  you (it won't). Unlike `std::unique_ptr<T>`,
13  `base::scoped_refptr<T>`, etc., it does not manage ownership or lifetime of
14  an allocated object.
15  - If this is the owner of the memory, consider using an alternative smart
16    pointer.
17- Don't assume `raw_ptr<T>` will protect you from freeing memory too early (it
18  likely will, but there are gotchas; one of them is that dereferencing will
19  result in other type of undefined behavior).
20
21(There are other, much more subtle rules that you should follow. They're also
22harder to violate. More about it in
23[the "Extra pointer rules" section](#Extra-pointer-rules).)
24
25`raw_ptr<T>` is a non-owning smart pointer that has improved memory-safety over
26raw pointers.  It behaves just like a raw pointer on platforms where
27ENABLE_BACKUP_REF_PTR_SUPPORT is off, and almost like one when it's on. The main
28difference is that when ENABLE_BACKUP_REF_PTR_SUPPORT is enabled, `raw_ptr<T>`
29is beneficial for security, because it can prevent a significant percentage of
30Use-after-Free (UaF) bugs from being exploitable. It does this by poisoning
31the freed memory and quarantining it as long as a dangling `raw_ptr<T>`
32exists.
33
34`raw_ptr<T>` has limited impact on stability - dereferencing
35a dangling pointer remains Undefined Behavior (although poisoning may
36lead to earlier, easier to debug crashes).
37
38`raw_ptr<T>` is a part of
39[the MiraclePtr project](https://docs.google.com/document/d/1pnnOAIz_DMWDI4oIOFoMAqLnf_MZ2GsrJNb_dbQ3ZBg/edit?usp=sharing)
40and currently implements
41[the BackupRefPtr algorithm](https://docs.google.com/document/d/1m0c63vXXLyGtIGBi9v6YFANum7-IRC3-dmiYBCWqkMk/edit?usp=sharing).
42If needed, please reach out to
43[memory-safety-dev@chromium.org](https://groups.google.com/a/chromium.org/g/memory-safety-dev)
44or (Google-internal)
45[chrome-memory-safety@google.com](https://groups.google.com/a/google.com/g/chrome-memory-safety)
46with questions or concerns.
47
48As of M116, it is enabled by default in all non-Renderer processes, on:
49- Android (incl. AndroidWebView, Android WebEngine)
50- Windows
51- ChromeOS (Ash), except for the `ExperimentalAsh` pointers
52- macOS
53- Linux
54
55In particular, it isn't enabled by default on those platforms:
56- iOS
57- ChromeOS (Lacros)
58- ChromeCast
59- Fuchsia
60- Aix
61- Zos
62
63[TOC]
64
65## When to use |raw_ptr&lt;T&gt;|
66
67[The Chromium C++ Style Guide](../../styleguide/c++/c++.md#non_owning-pointers-in-class-fields)
68asks to use `raw_ptr<T>` for class and struct fields in place of
69a raw C++ pointer `T*` whenever possible, except in Renderer-only code.
70This guide offers more details.
71
72The usage guidelines are currently enforced via Chromium Clang Plugin. We allow
73exclusions via:
74- `RAW_PTR_EXCLUSION` C++ attribute to exclude individual fields.  Examples:
75    - Cases where `raw_ptr<T>` won't compile (e.g. cases covered in
76      [the "Unsupported cases leading to compile errors" section](#Unsupported-cases-leading-to-compile-errors)).
77      Make sure to also look at
78      [the "Recoverable compile-time problems" section](#Recoverable-compile-time-problems).
79    - Cases where the pointer always points outside of PartitionAlloc
80      (e.g.  literals, stack allocated memory, shared memory, mmap'ed memory,
81      V8/Oilpan/Java heaps, TLS, etc.).
82    - (Very rare) cases that cause regression on perf bots.
83    - (Very rare) cases where `raw_ptr<T>` can lead to runtime errors.
84      Make sure to look at
85      [the "Extra pointer rules" section](#Extra-pointer-rules)
86      before resorting to this exclusion.
87- [RawPtrManualPathsToIgnore.h](../../tools/clang/plugins/RawPtrManualPathsToIgnore.h)
88  to exclude at a directory level (NOTE, use it as last resort, and be aware
89  it'll require a Clang plugin roll).  Examples:
90    - Renderer-only code (i.e. code in paths that contain `/renderer/` or
91      `third_party/blink/public/web/`)
92    - Code that cannot depend on `//base`
93    - Code in `//ppapi`
94- No explicit exclusions are needed for:
95    - `const char*`, `const wchar_t*`, etc.
96    - Function pointers
97    - ObjC pointers
98
99## Examples of using |raw_ptr&lt;T&gt;| instead of raw C++ pointers
100
101Consider an example struct that uses raw C++ pointer fields:
102
103```cpp
104struct Example {
105  int* int_ptr;
106  void* void_ptr;
107  SomeClass* object_ptr;
108  const SomeClass* ptr_to_const;
109  SomeClass* const const_ptr;
110};
111```
112
113When using `raw_ptr<T>` the struct above would look as follows:
114
115```cpp
116#include "base/memory/raw_ptr.h"
117
118struct Example {
119  raw_ptr<int> int_ptr;
120  raw_ptr<void> void_ptr;
121  raw_ptr<SomeClass> object_ptr;
122  raw_ptr<const SomeClass> ptr_to_const;
123  const raw_ptr<SomeClass> const_ptr;
124};
125```
126
127In most cases, only the type in the field declaration needs to change.
128In particular, `raw_ptr<T>` implements
129`operator->`, `operator*` and other operators
130that one expects from a raw pointer.
131Cases where other code needs to be modified are described in
132[the "Recoverable compile-time problems" section](#Recoverable-compile-time-problems)
133below.
134
135## Performance
136
137### Performance impact of using |raw_ptr&lt;T&gt;| instead of |T\*|
138
139Compared to a raw C++ pointer, on platforms where ENABLE_BACKUP_REF_PTR_SUPPORT
140is on, `raw_ptr<T>` incurs additional runtime
141overhead for initialization, destruction, and assignment (including
142`ptr++`, `ptr += ...`, etc.).
143There is no overhead when dereferencing or extracting a pointer (including
144`*ptr`, `ptr->foobar`, `ptr.get()`, or implicit conversions to a raw C++
145pointer).
146Finally, `raw_ptr<T>` has exactly the same memory footprint as `T*`
147(i.e. `sizeof(raw_ptr<T>) == sizeof(T*)`).
148
149One source of the performance overhead is
150a check whether a pointer `T*` points to a protected memory pool.
151This happens in `raw_ptr<T>`'s
152constructor, destructor, and assignment operators.
153If the pointed memory is unprotected,
154then `raw_ptr<T>` behaves just like a `T*`
155and the runtime overhead is limited to that extra check.
156(The security protection incurs additional overhead
157described in
158[the "Performance impact of enabling Use-after-Free protection" section](#Performance-impact-of-enabling-Use-after-Free-protection)
159below.)
160
161Some additional overhead comes from setting `raw_ptr<T>` to `nullptr`
162when default-constructed, destructed, or moved. (Yes, we said above to not rely
163on it, but to be precise this will always happen when
164ENABLE_BACKUP_REF_PTR_SUPPORT is on; no guarantees otherwise.)
165
166During
167[the "Big Rewrite"](https://groups.google.com/a/chromium.org/g/chromium-dev/c/vAEeVifyf78/m/SkBUc6PhBAAJ)
168most Chromium `T*` fields have been rewritten to `raw_ptr<T>`
169(excluding fields in Renderer-only code).
170The cumulative performance impact of such rewrite
171has been measured by earlier A/B binary experiments.
172There was no measurable impact, except that 32-bit platforms
173have seen a slight increase in jankiness metrics
174(for more detailed results see
175[the document here](https://docs.google.com/document/d/1MfDT-JQh_UIpSQw3KQttjbQ_drA7zw1gQDwU3cbB6_c/edit?usp=sharing)).
176
177### Performance impact of enabling Use-after-Free protection {#Performance-impact-of-enabling-Use-after-Free-protection}
178
179When the Use-after-Free protection is enabled, then `raw_ptr<T>` has some
180additional performance overhead.
181
182The protection can increase memory usage:
183- For each memory allocation Chromium's allocator (PartitionAlloc)
184  carves out extra 4 bytes. (That doesn't necessarily mean that each allocation
185  grows by 4B. Allocation sizes come from predefined buckets, so it's possible
186  for an allocation to stay within the same bucket and incur no additional
187  overhead, or hop over to the next bucket and incur much higher overhead.)
188- Freed memory is quarantined and not available for reuse as long
189  as dangling `raw_ptr<T>` pointers exist. (In practice this overhead has been
190  observed to be low, but on a couple occasions it led to significant memory
191  leaks, fortunately caught early.)
192
193The protection increases runtime costs - `raw_ptr<T>`'s constructor,
194destructor, and assignment operators need to maintain BackupRefPtr's ref-count
195(atomic increment/decrement). `ptr++`, `ptr += ...`, etc. don't need to do that,
196but instead have to incur the cost
197of verifying that resulting pointer stays within the same allocation (important
198for BRP integrity).
199
200## When it is okay to continue using raw C++ pointers
201
202### Unsupported cases leading to compile errors {#Unsupported-cases-leading-to-compile-errors}
203
204Continue to use raw C++ pointers in the following cases, which may otherwise
205result in compile errors:
206- Function pointers
207- Pointers to Objective-C objects
208- Pointer fields in classes/structs that are used as global, static, or
209  `thread_local` variables (see more details in the
210  [Rewrite exclusion statistics](https://docs.google.com/document/d/1uAsWnwy8HfIJhDPSh1efohnqfGsv2LJmYTRBj0JzZh8/edit#heading=h.dg4eebu87wg9)
211  )
212- Pointer fields in classes/structs that have to be trivially constructible or
213  destructible
214- Code that doesn’t depend on `//base` (including non-Chromium repositories and
215  third party libraries)
216- Code in `//ppapi`
217
218### Pointers to unprotected memory (performance optimization)
219
220Using `raw_ptr<T>` offers no security benefits (no UaF protection) for pointers
221that don’t point to protected memory (only PartitionAlloc-managed heap allocations
222in non-Renderer processes are protected).
223Therefore in the following cases raw C++ pointers may be used instead of
224`raw_ptr<T>`:
225- Pointer fields that can only point outside PartitionAlloc, including literals,
226  stack allocated memory, shared memory, mmap'ed memory, V8/Oilpan/Java heaps,
227  TLS, etc.
228- `const char*` (and `const wchar_t*`) pointer fields, unless you’re convinced
229  they can point to a heap-allocated object, not just a string literal
230- Pointer fields in certain renderer code. Specifically, we disallow usage in
231
232``` none
233third_party/blink/renderer/core/
234third_party/blink/renderer/platform/heap/
235third_party/blink/renderer/platform/wtf/
236```
237
238### Other perf optimizations
239
240As a performance optimization, raw C++ pointers may be used instead of
241`raw_ptr<T>` if it would have a significant
242[performance impact](#Performance).
243
244### Pointers in locations other than fields
245
246Use raw C++ pointers instead of `raw_ptr<T>` in the following scenarios:
247- Pointers in local variables and function parameters and return values. This
248  includes pointer fields in classes/structs that are used only on the stack.
249  (Using `raw_ptr<T>` here would cumulatively lead to performance regression and
250  the security benefit of UaF protection is lower for such short-lived
251  pointers.)
252- Pointer fields in unions. However, note that a much better, modern alternative
253  is `absl::variant` + `raw_ptr<T>`. If use of C++ union is absolutely
254  unavoidable, prefer a regular C++ pointer: incorrect management of a
255  `raw_ptr<T>` field can easily lead to refcount corruption.
256- Pointers whose addresses are used only as identifiers and which are
257  never dereferenced (e.g. keys in a map). There is a performance gain
258  by not using `raw_ptr` in this case; prefer to use `uintptr_t` to
259  emphasize that the entity can dangle and must not be dereferenced. (NOTE,
260  this is a dangerous practice irrespective of raw_ptr usage, as there is a risk
261  of memory being freed and another pointer allocated with the same address!)
262
263You don’t have to, but may use `raw_ptr<T>`, in the following scenarios:
264- Pointers that are used as an element type of collections/wrappers. E.g.
265  `std::vector<T*>` and `std::vector<raw_ptr<T>>` are both okay, but prefer the
266  latter if the collection is a class field (note that some of the perf
267  optimizations above might still apply and argue for using a raw C++ pointer).
268
269
270## Extra pointer rules {#Extra-pointer-rules}
271
272`raw_ptr<T>` requires following some extra rules compared to a raw C++ pointer:
273- Don’t assign invalid, non-null addresses (this includes previously valid and
274  now freed memory,
275  [Win32 handles](https://crbug.com/1262017), and more). You can only assign an
276  address of memory that is valid at the time of assignment. Exceptions:
277    - a pointer to the end of a valid allocation (but not even 1 byte further)
278    - a pointer to the last page of the address space, e.g. for sentinels like
279      `reinterpret_cast<void*>(-1)`
280- Don’t initialize or assign `raw_ptr<T>` memory directly
281  (e.g. `reinterpret_cast<ClassWithRawPtr*>(buffer)` or
282  `memcpy(reinterpret_cast<void*>(&obj_with_raw_ptr), buffer)`.
283- Don’t assign to a `raw_ptr<T>` concurrently, even if the same value.
284- Don’t rely on moved-from pointers to keep their old value. Unlike raw
285  pointers, `raw_ptr<T>` may be cleared upon moving.
286- Don't use the pointer after it is destructed. Unlike raw pointers,
287  `raw_ptr<T>` may be cleared upon destruction. This may happen e.g. when fields
288  are ordered such that the pointer field is destructed before the class field
289  whose destructor uses that pointer field (e.g. see
290  [Esoteric Issues](https://docs.google.com/document/d/14Ol_adOdNpy4Ge-XReI7CXNKMzs_LL5vucDQIERDQyg/edit#heading=h.yoba1l8bnfmv)).
291- Don’t assign to a `raw_ptr<T>` until its constructor has run. This may happen
292  when a base class’s constructor uses a not-yet-initialized field of a derived
293  class (e.g. see
294  [Applying MiraclePtr](https://docs.google.com/document/d/1cnpd5Rwesq7DCZiD8FIJfPGHvQN3-Gul6xib_4hwfBg/edit?ts=5ed2d317#heading=h.4ry5d9a6fuxs)).
295
296Some of these would result in undefined behavior (UB) even in the world without
297`raw_ptr<T>` (e.g. see
298[Field destruction order](https://groups.google.com/a/chromium.org/g/memory-safety-dev/c/3sEmSnFc61I/m/ZtaeWGslAQAJ)),
299but you’d likely get away without any consequences. In the `raw_ptr<T>` world,
300an obscure crash may occur. Those crashes often manifest themselves as SEGV or
301`CHECK` inside `RawPtrBackupRefImpl::AcquireInternal()` or
302`RawPtrBackupRefImpl::ReleaseInternal()`, but you may also experience memory
303corruption or a silent drop of UaF protection.
304
305## Pointer Annotations
306
307### The AllowPtrArithmetic trait
308
309In an ideal world, a raw_ptr would point to a single object, rather than to
310a C-style array of objects accessed via pointer arithmetic, since the latter
311is best handled via a C++ construct such as base::span<> or std::vector<>.
312raw_ptrs upon which such operations are performed and for which conversion is
313desirable have been tagged with the AllowPtrArithmetic trait. That all such
314pointer are tagged can be enforced by setting the GN build arg
315enable_pointer_arithmetic_trait_check=true.
316
317### The AllowUninitialized trait
318
319When building Chromium, raw_ptrs are always nullptr initialized, either as
320the result of specific implementation that requires it (e.g. BackupRefPtr),
321or as the result of build flags (to enforce consistency). However, we provide
322an opt-out to allow third-party code to skip this step (where possible). Use
323this trait sparingly.
324
325## Recoverable compile-time problems {#Recoverable-compile-time-problems}
326
327### Explicit |raw_ptr.get()| might be needed
328
329If a raw pointer is needed, but an implicit cast from `raw_ptr<SomeClass>` to
330`SomeClass*` doesn't work, then the raw pointer needs to be obtained by explicitly
331calling `.get()`. Examples:
332- `auto* raw_ptr_var = wrapped_ptr_.get()` (`auto*` requires the initializer to
333  be a raw pointer)
334    - Alternatively you can change `auto*` to `auto&`. Avoid using `auto` as it’ll
335      copy the pointer, which incurs a performance overhead.
336- `return condition ? raw_ptr : wrapped_ptr_.get();` (ternary operator needs
337  identical types in both branches)
338- `TemplatedFunction(wrapped_ptr_.get());` (implicit cast doesn't kick in for
339  `T*` arguments in templates)
340- `printf("%p", wrapped_ptr_.get());` (can't pass class type arguments to
341  variadic functions)
342- `reinterpret_cast<SomeClass*>(wrapped_ptr_.get())` (`const_cast` and
343  `reinterpret_cast` sometimes require their argument to be a raw pointer;
344  `static_cast` should "Just Work")
345- `T2 t2 = t1_wrapped_ptr_.get();` (where there is an implicit conversion
346  constructor `T2(T1*)` the compiler can handle one implicit conversion, but not
347  two)
348- In general, when type is inferred by a compiler and then used in a context
349  where a pointer is expected.
350
351### Out-of-line constructor/destructor might be needed
352
353Out-of-line constructor/destructor may be newly required by the chromium style
354clang plugin.  Error examples:
355- `error: [chromium-style] Complex class/struct needs an explicit out-of-line
356  destructor.`
357- `error: [chromium-style] Complex class/struct needs an explicit out-of-line
358  constructor.`
359
360`raw_ptr<T>` uses a non-trivial constructor/destructor, so classes that used to
361be POD or have a trivial destructor may require an out-of-line
362constructor/destructor to satisfy the chromium style clang plugin.
363
364
365### In-out arguments need to be refactored
366
367Due to implementation difficulties,
368`raw_ptr<T>` doesn't support an address-of operator.
369This means that the following code will not compile:
370
371```cpp
372void GetSomeClassPtr(SomeClass** out_arg) {
373  *out_arg = ...;
374}
375
376struct MyStruct {
377  void Example() {
378    GetSomeClassPtr(&wrapped_ptr_);  // <- won't compile
379  }
380
381  raw_ptr<SomeClass> wrapped_ptr_;
382};
383```
384
385The typical fix is to change the type of the out argument
386(see also [an example CL here](https://crrev.com/c/4545743)):
387
388```cpp
389void GetSomeClassPtr(raw_ptr<SomeClass>* out_arg) {
390  *out_arg = ...;
391}
392```
393
394Similarly this code:
395
396```cpp
397void FillPtr(SomeClass*& out_arg) {
398  out_arg = ...;
399}
400```
401
402would have to be changed to this:
403
404```cpp
405void FillPtr(raw_ptr<SomeClass>& out_arg) {
406  out_arg = ...;
407}
408```
409
410Similarly this code:
411
412```cpp
413SomeClass*& GetPtr() {
414  return wrapper_ptr_;
415}
416```
417
418would have to be changed to this:
419
420```cpp
421raw_ptr<SomeClass>& GetPtr() {
422  return wrapper_ptr_;
423}
424```
425
426
427In case you cannot refactor the in-out arguments (e.g. third party library), you
428may use `raw_ptr.AsEphemeralRawAddr()` to obtain *extremely* short-lived
429`T**` or `T*&`. You should not treat `T**` obtained via
430`raw_ptr.AsEphemeralRawAddr()` as a normal pointer pointer, and must follow
431these requirements.
432
433- Do NOT store `T**` or `T*&` anywhere, even as a local variable.
434  - It will become invalid very quickly and can cause dangling pointer issue
435- Do NOT use `raw_ptr<T>`, `T**` or `T*&` multiple times within an expression.
436  - The implementation assumes raw_ptr<T> is never accessed when `T**` or `T*&`
437    is alive.
438
439```cpp
440void GetSomeClassPtr(SomeClass** out_arg) {
441  *out_arg = ...;
442}
443void FillPtr(SomeClass*& out_arg) {
444  out_arg = ...;
445}
446void Foo() {
447  raw_ptr<SomeClass> ptr;
448  GetSomeClassPtr(&ptr.AsEphemeralRawAddr());
449  FillPtr(ptr.AsEphemeralRawAddr()); // Implicitly converted into |SomeClass*&|.
450}
451```
452
453Technically, `raw_ptr.AsEphemeralRawAddr()` generates a temporary instance of
454`raw_ptr<T>::EphemeralRawAddr`, which holds a temporary copy of `T*`.
455`T**` and `T*&` points to a copied version of the original pointer and
456any modification made via `T**` or `T*&` is written back on destruction of
457`EphemeralRawAddr` instance.
458C++ guarantees a temporary object returned by `raw_ptr.AsEphemeralRawAddr()`
459lives until completion of evaluation of "full-expression" (i.e. the outermost
460expression). This makes it possible to use `T**` and `T*&` within single
461expression like in-out param.
462
463```cpp
464struct EphemeralRawAddr {
465  EphemeralRawAddr(raw_ptr& ptr): copy(ptr.get()), original(ptr) {}
466  ~EphemeralRawAddr() {
467    original = copy;
468    copy = nullptr;
469  }
470
471  T** operator&() { return &copy; }
472  operator T*&() { return copy; }
473
474  T* copy;
475  raw_ptr& original;  // Original pointer.
476};
477```
478
479
480### Modern |nullptr| is required
481
482As recommended by the Google C++ Style Guide,
483[use nullptr instead of NULL](https://google.github.io/styleguide/cppguide.html#0_and_nullptr/NULL) -
484the latter might result in compile-time errors when used with `raw_ptr<T>`.
485
486Example:
487
488```cpp
489struct SomeStruct {
490  raw_ptr<int> ptr_field;
491};
492
493void bar() {
494  SomeStruct some_struct;
495  some_struct.ptr_field = NULL;
496}
497```
498
499Error:
500```err
501../../base/memory/checked_ptr_unittest.cc:139:25: error: use of overloaded
502operator '=' is ambiguous (with operand types raw_ptr<int>' and 'long')
503  some_struct.ptr_field = NULL;
504  ~~~~~~~~~~~~~~~~~~~~~ ^ ~~~~
505../../base/memory/raw_ptr.h:369:29: note: candidate function
506  ALWAYS_INLINE raw_ptr& operator=(std::nullptr_t) noexcept {
507                         ^
508../../base/memory/raw_ptr.h:374:29: note: candidate function
509  ALWAYS_INLINE raw_ptr& operator=(T* p)
510                         noexcept {
511```
512
513### [rare] Explicit overload or template specialization for |raw_ptr&lt;T&gt;|
514
515In rare cases, the default template code won’t compile when `raw_ptr<...>` is
516substituted for a template argument.  In such cases, it might be necessary to
517provide an explicit overload or template specialization for `raw_ptr<T>`.
518
519Example (more details in
520[Applying MiraclePtr](https://docs.google.com/document/d/1cnpd5Rwesq7DCZiD8FIJfPGHvQN3-Gul6xib_4hwfBg/edit?ts=5ed2d317#heading=h.o2pf3fg0zzf) and the
521[Add CheckedPtr support for cbor_extract::Element](https://chromium-review.googlesource.com/c/chromium/src/+/2224954)
522CL):
523
524```cpp
525// An explicit overload (taking raw_ptr<T> as an argument)
526// was needed below:
527template <typename S>
528constexpr StepOrByte<S> Element(
529    const Is required,
530    raw_ptr<const std::string> S::*member,  // <- HERE
531    uintptr_t offset) {
532  return ElementImpl<S>(required, offset, internal::Type::kString);
533}
534```
535
536## AddressSanitizer support
537
538For years, AddressSanitizer has been the main tool for diagnosing memory
539corruption issues in Chromium. MiraclePtr alters the security properties of some
540of some such issues, so ideally it should be integrated with ASan. That way an
541engineer would be able to check whether a given use-after-free vulnerability is
542covered by the protection without having to switch between ASan and non-ASan
543builds.
544
545Unfortunately, MiraclePtr relies heavily on PartitionAlloc, and ASan needs its
546own allocator to work. As a result, the default implementation of `raw_ptr<T>`
547can't be used with ASan builds. Instead, a special version of `raw_ptr<T>` has
548been implemented, which is based on the ASan quarantine and acts as a
549sufficiently close approximation for diagnostic purposes. At crash time, the
550tool will tell the user if the dangling pointer access would have been protected
551by MiraclePtr *in a regular build*.
552
553You can configure the diagnostic tool by modifying the parameters of the feature
554flag `PartitionAllocBackupRefPtr`. For example, launching Chromium as follows:
555
556```
557path/to/chrome --enable-features=PartitionAllocBackupRefPtr:enabled-processes/browser-only/asan-enable-dereference-check/true/asan-enable-extraction-check/true/asan-enable-instantiation-check/true
558```
559
560activates all available checks in the browser process.
561
562### Available checks
563
564MiraclePtr provides ASan users with three kinds of security checks, which differ
565in when a particular check occurs:
566
567#### Dereference
568
569This is the basic check type that helps diagnose regular heap-use-after-free
570bugs. It's enabled by default.
571
572#### Extraction
573
574The user will be warned if a dangling pointer is extracted from a `raw_ptr<T>`
575variable. If the pointer is then dereferenced, an ASan error report will follow.
576In some cases, extra work on the reproduction case is required to reach the
577faulty memory access. However, even without memory corruption, relying on the
578value of a dangling pointer may lead to problems. For example, it's a common
579(anti-)pattern in Chromium to use a raw pointer as a key in a container.
580Consider the following example:
581
582```
583std::map<T*, std::unique_ptr<Ext>> g_map;
584
585struct A {
586  A() {
587    g_map[this] = std::make_unique<Ext>(this);
588  }
589
590  ~A() {
591    g_map.erase(this);
592  }
593};
594
595raw_ptr<A> dangling = new A;
596// ...
597delete dangling.get();
598A* replacement = new A;
599// ...
600auto it = g_map.find(dangling);
601if (it == g_map.end())
602  return 0;
603it->second.DoStuff();
604```
605
606Depending on whether the allocator reuses the same memory region for the second
607`A` object, the program may inadvertently call `DoStuff()` on the wrong `Ext`
608instance. This, in turn, may corrupt the state of the program or bypass security
609controls if the two `A` objects belong to different security contexts.
610
611Given the proportion of false positives reported in the mode, it is disabled by
612default. It's mainly intended to be used by security researchers who are willing
613to spend a significant amount of time investigating these early warnings.
614
615#### Instantiation
616
617This check detects violations of the rule that when instantiating a `raw_ptr<T>`
618from a `T*` , it is only allowed if the `T*` is a valid (i.e. not dangling)
619pointer. This rule exists to help avoid an issue called "pointer laundering"
620which can result in unsafe `raw_ptr<T>` instances that point to memory that is
621no longer in quarantine. This is important, since subsequent use of these
622`raw_ptr<T>` might appear to be safe.
623
624In order for "pointer laundering" to occur, we need (1) a dangling `T*`
625(pointing to memory that has been freed) to be assigned to a `raw_ptr<T>`, while
626(2) there is no other `raw_ptr<T>` pointing to the same object/allocation at the
627time of assignment.
628
629The check only detects (1), a dangling `T*` being assigned to a `raw_ptr<T>`, so
630in order to determine whether "pointer laundering" has occurred, we need to
631determine whether (2) could plausibly occur, not just in the specific
632reproduction testcase, but in the more general case.
633
634In the absence of thorough reasoning about (2), the assumption here should be
635that any failure of this check is a security issue of the same severity as an
636unprotected use-after-free.
637
638### Protection status
639
640When ASan generates a heap-use-after-free report, it will include a new section
641near the bottom, which starts with the line `MiraclePtr Status: <status>`. At
642the moment, it has three possible options:
643
644#### Protected
645
646The system is sufficiently confident that MiraclePtr makes the discovered issue
647unexploitable. In the future, the security severity of such bugs will be
648reduced.
649
650#### Manual analysis required
651
652Dangling pointer extraction was detected before the crash, but there might be
653extra code between the extraction and dereference. Most of the time, the code in
654question will look similar to the following:
655
656```
657struct A {
658  raw_ptr<T> dangling_;
659};
660
661void trigger(A* a) {
662  // ...
663  T* local = a->dangling_;
664  DoStuff();
665  local->DoOtherStuff();
666  // ...
667}
668```
669
670In this scenario, even though `dangling_` points to freed memory, that memory
671is protected and will stay in quarantine until `dangling_` (and all other
672`raw_ptr<T>` variables pointing to the same region) changes its value or gets
673destroyed. Therefore, the expression `a_->dangling->DoOtherStuff()` wouldn't
674trigger an exploitable use-after-free.
675
676You will need to make sure that `DoStuff()` is sufficiently trivial and can't
677(not only for the particular reproduction case, but *even in principle*) make
678`dangling_` change its value or get destroyed. If that's the case, the
679`DoOtherStuff()` call may be considered protected. The tool will provide you
680with the stack trace for both the extraction and dereference events.
681
682#### Not protected
683
684The dangling `T*` doesn't appear to originate from a `raw_ptr<T>` variable,
685which means MiraclePtr can't prevent this issue from being exploited. In
686practice, there may still be a `raw_ptr<T>` in a different part of the code that
687protects the same allocation indirectly, but such protection won't be considered
688robust enough to impact security-related decisions.
689
690### Limitations
691
692The main limitation of MiraclePtr in ASan builds is the main limitation of ASan
693itself: the capacity of the quarantine is limited. Eventually, every allocation
694in quarantine will get reused regardless of whether there are still references
695to it.
696
697In the context of MiraclePtr combined with ASan, it's a problem when:
698
6991. A heap allocation that isn't supported by MiraclePtr is made. At the moment,
700   the only such class is allocations made early during the process startup
701   before MiraclePtr can be activated.
7022. Its address is assigned to a `raw_ptr<T>` variable.
7033. The allocation gets freed.
7044. A new allocation is made in the same memory region as the first one, but this
705   time it is supported.
7065. The second allocation gets freed.
7076. The `raw_ptr<T>` variable is accessed.
708
709In this case, MiraclePtr will incorrectly assume the memory access is protected.
710Luckily, considering the small number of unprotected allocations in Chromium,
711the size of the quarantine, and the fact that most reproduction cases take
712relatively short time to run, the odds of this happening are very low.
713
714The problem is relatively easy to spot if you look at the ASan report: the
715allocation and deallocation stack traces won't be consistent across runs and
716the allocation type won't match the use stack trace.
717
718If you encounter a suspicious ASan report, it may be helpful to re-run Chromium
719with an increased quarantine capacity as follows:
720
721```
722ASAN_OPTIONS=quarantine_size_mb=1024 path/to/chrome
723```
724
725## Appendix: Is raw_ptr Live?
726
727![Diagram showing how both code support and feature flag must be present
728  for raw_ptr to be BRP.](./raw_ptr_liveness.png)
729
730Note that
731
732*   [`RawPtrNoOpImpl`][raw-ptr-noop-impl] is thought to have no
733    overhead. However, this has yet to be verified.
734
735*   "Inert BackupRefPtr" _has_ overhead - once BRP support is compiled
736    in, every `raw_ptr` will (at assignment) perform the
737    check that asks, ["is BRP protection active?"][is-brp-active]
738
739As for general BRP enablement,
740
741*   BRP is live in most browser tests and Chromium targets.
742
743    *   This is nuanced by platform type and process type.
744
745*   In unit tests,
746
747    *   `raw_ptr` is the no-op impl when the build is ASan.
748
749    *   `raw_ptr` is live BRP on bots.
750
751    *   `raw_ptr` is inert BRP otherwise (see https://crbug.com/1440658).
752
753[raw-ptr-noop-impl]: https://source.chromium.org/search?q=class:RawPtrNoOpImpl&ss=chromium
754[is-brp-active]: https://source.chromium.org/search?q=func:RawPtrBackupRefImpl::IsSupportedAndNotNull&ss=chromium
755