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<T>| 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<T>| 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<T>| 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 © } 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<T>| 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 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